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
长,因此预测的轨迹会更长。我如何才能将轨迹转换为使用像素或 CGFloat
s?我可以除以像素与米的比率吗?
(从物理学的角度回答,我不是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)
我想计算一个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
长,因此预测的轨迹会更长。我如何才能将轨迹转换为使用像素或 CGFloat
s?我可以除以像素与米的比率吗?
(从物理学的角度回答,我不是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)