预测轨迹

预测轨迹传统上是一项令人困惑的工作,一些开发者只是“猜测”并希望能做到最好。在本教程中,我们将讨论真正的解决方案!

测试项目设置

为了本教程的目的,让我们快速创建一个测试项目。

  1. 打开 Corona 模拟器。

  2. 从欢迎窗口点击**新建项目**,或从**文件**菜单中选择新建项目…

  3. 在项目/应用程序名称中,键入 `TestLaunch` 并确保选中**空白**模板选项。将屏幕尺寸设置保留为默认值,但将**默认方向**设置为**横向**,然后点击**确定**(Windows) 或**下一步**(Mac)。这将在您指定的位置(文件夹)中创建测试项目的基本文件。

  4. 找到项目文件夹,在您选择的文本编辑器中打开 `main.lua` 文件,并插入以下代码行

local physics = require( "physics" )
physics.start()  -- Start the physics engine
physics.setGravity( 0, 9.8 )  -- Set typical Earth gravity

-- Create display group for predicted trajectory
local predictedPath = display.newGroup()

-- Create function forward references
local getTrajectoryPoint
local launchProjectile

触摸处理

预测轨迹的核心任务是收集一些基本数据,例如要发射的抛射物的起始位置和起始速度。这可以通过 `“touch”` 事件监听器来完成,因此在之前的命令之后将以下代码添加到您的测试项目中

local function screenTouch( event )

    if ( event.phase == "moved" ) then

        -- Remove trajectory path group (to clear previous path), then re-create it
        display.remove( predictedPath )
        predictedPath = display.newGroup()

        local startingVelocity = { x=event.x-event.xStart, y=event.y-event.yStart }

        for i = 1,240,2 do
            local s = { x=event.xStart, y=event.yStart }
            local trajectoryPosition = getTrajectoryPoint( s, startingVelocity, i )
            local dot = display.newCircle( predictedPath, trajectoryPosition.x, trajectoryPosition.y, 4 )
        end

    elseif ( event.phase == "ended" ) then

        launchProjectile( event )
    end
    return true
end
Runtime:addEventListener( "touch", screenTouch )

让我们更详细地检查这段代码

  1. 此函数的核心作用于 `“moved”` 触摸阶段。这允许我们开始触摸屏幕上的某个点(这将是抛射物的起点),然后四处拖动以渲染表示预测轨迹的虚线路径。

  2. 在条件 `“moved”` 阶段块内部,我们首先移除先前创建的 `predictedPath` 显示组,然后我们重新创建它。移除该组然后立即重新创建它似乎很奇怪,但这是一种在绘制表示调整后轨迹的全新集合之前移除所有先前绘制的点的一种简单方法。

  3. 之后,我们通过从当前触摸位置 (`event.x`/`event.y`) 中减去起始触摸位置 (`event.xStart`/`event.yStart`) 来计算起始 **x** 和 **y** “速度”值。

  4. 最后,利用这些基本信息,我们运行一个 `for` 循环,该循环以 240 步(以每秒 60 帧的速度发射 4 秒)绘制预测的轨迹路径,使用矢量圆圈(如果需要,您可以使用图像)。请注意,我们在循环中以 `2` 为步长进行迭代,因为出于测试目的,120 个路径点(点)足以清晰地表示预测的轨迹。

计算轨迹

在上述函数的 `for` 循环中,核心计算是通过以下函数完成的,该函数使用每秒帧数作为“时间步长”,以及起始位置和速度。将此代码块添加到您项目中先前行的后面

getTrajectoryPoint = function( startingPosition, startingVelocity, n )

    -- Velocity and gravity are given per second but we want time step values
    local t = 1/display.fps  -- Seconds per time step at 60 frames-per-second (default)
    local stepVelocity = { x=t*startingVelocity.x, y=t*startingVelocity.y }
    local gx, gy = physics.getGravity()
    local stepGravity = { x=t*gx, y=t*gy }

    return {
        x = startingPosition.x + n * stepVelocity.x + 0.25 * (n*n+n) * stepGravity.x,
        y = startingPosition.y + n * stepVelocity.y + 0.25 * (n*n+n) * stepGravity.y
    }
end
注意

此轨迹计算基于以 60每秒帧数 (`config.lua` 中的 `fps` 值)运行的应用程序。如果您希望使用每秒 30 帧,请将返回的 `x` 和 `y` 值中的每个 `0.25` 实例更改为 `0.5`,一切都应该完美匹配。

发射抛射物

触摸释放时,发射抛射物很简单。我们只需生成抛射物,添加一个物理实体,并分配我们收集的速度值,使其沿着轨迹路径完美匹配地飞行。要进行测试,请将此代码块添加到您项目中先前行的后面

launchProjectile = function( event )
    
    -- Create projectile object
    local proj = display.newCircle( event.xStart, event.yStart, 24 )
    proj:setFillColor( 1,0.2,0.2 )

    -- Add physical body to object
    physics.addBody( proj, { bounce=0.2, density=1.0, radius=24 } )

    -- Apply velocity values to object
    local vx, vy = event.x-event.xStart, event.y-event.yStart
    proj:setLinearVelocity( vx, vy )
end

现在保存项目,刷新/重新加载它,并触摸/拖动屏幕以绘制预测的轨迹路径。释放后,一个抛射物(红色圆圈)应该在起点生成,并且它应该完美地遵循预测的路径!

结论

希望本教程能够说明预测轨迹并非一项不可逾越的任务,也不是一项需要“最佳猜测”操作的任务。通过这些计算,在 Corona 中可以轻松实现准确的轨迹路径!