Swift: 计算带有施加脉冲的节点的轨迹

Swift: Calculate trajectory of node with applied impulse

我想计算一个SKSpriteNode在受到冲击后的轨迹。我有 12 个球,我想把它们放在球发射后的位置。我正在使用等式 (t)=₀+t(₀+½Δt)+½t²

我对节点施加的冲量是基于用户在屏幕上拖动的长度。

这是我试过的:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches{
            let location = touch.location(in: self)
            startPositionDrag = location
        }
    }

touchesMoved 中,我更新轨迹节点以显示如果在该位置启动节点将去向的位置。

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
   for touch in touches{

        movedLocation = touch.location(in: self) 

        calculateTrajectoryy(velocityY: (startPositionDrag.y - movedLocation.y), velocityX: (startPositionDrag.x - movedLocation.x))

     }

这是我使用方程式的地方。我有一个 12 个球的数组。我使用 i 作为时间,因为我想将每个球放在 i 秒后节点所在的位置。

func calculateTrajectoryy(velocityY: CGFloat, velocityX: CGFloat){

        for i in 0...12{

            let time = CGFloat(i)
            let accelerationX = CGFloat(0)
            let accelerationY = CGFloat(-9.8)
            var x = CGFloat()
            var y = CGFloat()

            // (t)=₀+t(₀+½Δt)+½t²
            x = ball.position.x + time * (velocityX + 0.5 * time * accelerationX) + 0.5 * time * time * accelerationX

            y = ball.position.y + time * (velocityY + 0.5 * time * accelerationY) + 0.5 * time * time * accelerationY

            arrray[i].position = CGPoint(x: x, y: y)

        }
    }

最后在 touchesEnded 中,当用户松开拖动时,节点将应用一个脉冲,它应该完全遵循创建的球的路径。

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches{
            let location = touch.location(in: self)

               ball.physicsBody?.applyImpulse(CGVector(dx:(startPositionDrag.x - endPositionDrag.x), dy:(startPositionDrag.y - endPositionDrag.y))

            }
        }
    }

球不遵循球产生的弧线。球射出一点点,而球的弧线又大又长。我认为这可能与单位转换有关。该等式使用每秒米数的速度值,而向后拖的位置是 CGFloat。米会比 CGFloat 长,因此预测的轨迹会更长。我如何才能将轨迹转换为使用像素或 CGFloats?我可以除以像素与米的比率吗?

(从物理学的角度回答,我不是swift或sprite-kit的专家)

'units'(以及一个点代表的物理距离)的选择完全由您决定,您在调用 applyImpulse() 之前所拥有的数学与假设 1point = 1meter 是一致的.这可能是您想要的,也可能不是您想要的,但这是一个不错的选择 - 顺便说一句,在您的代码中唯一(某种程度上)将一个点固定到真实世界的仪表的是重力值 9.8,否则您可以只是称它们为点,实际意义没有变化——物理引擎并不关心。

您在代码中放入数组[] 的连续球位置是假设发射的球将在一秒钟内覆盖从 start_of_drag 到 end_of_drag 的距离(因为您只需将距离用作速度值,即 1m 变为 1m/sec,2m = 2m/s,等等)。 (注意我不太清楚 12 点预测路径在您的情况下如何正确工作,因为要获得指向拖动方向的矢量,您通常会 end_drag - start_drag,而您的代码会 start - end)

您遇到的问题可能与 applyImpulse 期望以牛顿*秒为单位的值有关(并且速度变化将取决于球的质量:delta-V = 脉冲/质量) ,而您使用相同的 "distance-that-became-velocity" 单位调用函数。

如果球在拖动之前始终是静止的,并且您希望它立即开始以选定的速度移动到某个地方,则简单地将其速度分配给所需值并让它飞起来可能更容易,而不是尝试使用 applyImpulse,例如:

(编辑:正如@knight0fdragon 指出的那样,SK 的距离单位是点,但速度是以抽象单位 'meter' = 150pt/sec 来衡量的,所以如果你用点和 points/sec 到目前为止,您必须在修改 physicsBody 向量之前缩放速度:将其除以 150.0)。

ball.physicsBody?.velocity = CGVector(dx:velocityX, dy:velocityY)

如果球已经有一些速度并且你想修改它一些量(对你来说用速度单位而不是 N*m 表示)并且忽略质量,将速度变化添加到球的当前速度。

只是为了展示我是如何将 Leo K 的答案应用到代码中的。

不知何故,我使用了像素与英寸比率的 150,并将其变为 1.5,除以 1.5 得到轨迹,再乘以它得到冲量。它几乎准确地预测了节点的路径。

    calculateTrajectoryy(velocityY: (startPositionDrag.y - movedLocation.y) / 1.5, velocityX: (startPositionDrag.x - movedLocation.x) / 1.5)

    ball.physicsBody?.applyImpulse(CGVector(dx:(startPositionDrag.x - endPositionDrag.x) * 1.5, dy:(startPositionDrag.y - endPositionDrag.y) * 1.5)