使用 PhysicsBody 旋转 SCNNode 以匹配 Thumbstick 的弧度
Use PhysicsBody to rotate an SCNNode to match the Thumbstick's radian
我正在开发一款自上而下的 space 游戏,使用 Swift 和 SceneKit 构建,设置如下:
SCNNode 表示 spaceship
- 旋转被限制在y轴上;值范围从
-M_PI_2
到 M_PI + M_PI_2
- 移动被限制在 x 轴和 z 轴上。
游戏控制器摇杆输入
- x 轴和 y 轴的值范围从
-1.0
到 1.0
。
当游戏控制器的摇杆改变位置时,space飞船应使用物理体旋转以匹配摇杆的弧度。
摇杆的目标弧度可以通过以下公式计算:
let targetRadian = M_PI_2 + atan2(-y, -x)
节点当前弧度可以通过以下方式获取:
let currentRadian = node.presentationNode.rotation.w * node.presentationNode.rotation.y
NSTimeInterval deltaTime
提供自上次旋转计算以来的时间(以秒为单位)。
如何使用angularVelocity
、applyTorque
或其他物理方法旋转节点以达到targetRadian
?
targetRadian
和 currentRadian
之间的差异在 0.0
到 -2π
之间,具体取决于 currentRadian
的值。此等式将确定转弯的最短方向 .Clockwise
或 .CounterClockwise
,以到达 targetRadian
:
let turnDirection = (radianDifference + (M_PI * 2)) % (M_PI * 2) < M_PI ? RotationDirection.CounterClockwise : RotationDirection.Clockwise
使用 applyTorque
,有可能过度旋转超过 targetRadian
,导致摇摆效果,就像指南针向一个点磁化一样,因为旋转来回改变方向到达 targetRadian
。以下虽然不是完美的解决方案,但抑制了效果:
let turnDampener = abs(radianDifference) < 1.0 ? abs(radianDifference) : 1.0
完整的解决方案是:
enum RotationDirection: Double {
case Clockwise = -1.0
case CounterClockwise = 1.0
}
func rotateNodeTowardDirectionalVector(node: SCNNode, targetDirectionalVector: (x: Double, y: Double), deltaTime: NSTimeInterval) {
guard abs(targetDirectionalVector.x) > 0.0 || abs(targetDirectionalVector.y) > 0.0 else { return }
let currentRadian = Double(node.presentationNode.rotation.w * node.presentationNode.rotation.y)
let targetRadian = M_PI_2 + atan2(-targetDirectionalVector.y, -targetDirectionalVector.x)
let radianDifference = targetRadian - currentRadian
let π2 = M_PI * 2
let turnDirection = (radianDifference + π2) % π2 < M_PI ? RotationDirection.CounterClockwise : RotationDirection.Clockwise
let absRadianDifference = abs(radianDifference)
let turnDampener = absRadianDifference < 1.0 ? absRadianDifference : 1.0
node.physicsBody?.applyTorque(SCNVector4Make(0, CGFloat(turnDirection.rawValue), 0, CGFloat(deltaTime * turnDampener)), impulse: true)
}
我正在开发一款自上而下的 space 游戏,使用 Swift 和 SceneKit 构建,设置如下:
SCNNode 表示 spaceship
- 旋转被限制在y轴上;值范围从
-M_PI_2
到M_PI + M_PI_2
- 移动被限制在 x 轴和 z 轴上。
游戏控制器摇杆输入
- x 轴和 y 轴的值范围从
-1.0
到1.0
。
当游戏控制器的摇杆改变位置时,space飞船应使用物理体旋转以匹配摇杆的弧度。
摇杆的目标弧度可以通过以下公式计算:
let targetRadian = M_PI_2 + atan2(-y, -x)
节点当前弧度可以通过以下方式获取:
let currentRadian = node.presentationNode.rotation.w * node.presentationNode.rotation.y
NSTimeInterval deltaTime
提供自上次旋转计算以来的时间(以秒为单位)。
如何使用angularVelocity
、applyTorque
或其他物理方法旋转节点以达到targetRadian
?
targetRadian
和 currentRadian
之间的差异在 0.0
到 -2π
之间,具体取决于 currentRadian
的值。此等式将确定转弯的最短方向 .Clockwise
或 .CounterClockwise
,以到达 targetRadian
:
let turnDirection = (radianDifference + (M_PI * 2)) % (M_PI * 2) < M_PI ? RotationDirection.CounterClockwise : RotationDirection.Clockwise
使用 applyTorque
,有可能过度旋转超过 targetRadian
,导致摇摆效果,就像指南针向一个点磁化一样,因为旋转来回改变方向到达 targetRadian
。以下虽然不是完美的解决方案,但抑制了效果:
let turnDampener = abs(radianDifference) < 1.0 ? abs(radianDifference) : 1.0
完整的解决方案是:
enum RotationDirection: Double {
case Clockwise = -1.0
case CounterClockwise = 1.0
}
func rotateNodeTowardDirectionalVector(node: SCNNode, targetDirectionalVector: (x: Double, y: Double), deltaTime: NSTimeInterval) {
guard abs(targetDirectionalVector.x) > 0.0 || abs(targetDirectionalVector.y) > 0.0 else { return }
let currentRadian = Double(node.presentationNode.rotation.w * node.presentationNode.rotation.y)
let targetRadian = M_PI_2 + atan2(-targetDirectionalVector.y, -targetDirectionalVector.x)
let radianDifference = targetRadian - currentRadian
let π2 = M_PI * 2
let turnDirection = (radianDifference + π2) % π2 < M_PI ? RotationDirection.CounterClockwise : RotationDirection.Clockwise
let absRadianDifference = abs(radianDifference)
let turnDampener = absRadianDifference < 1.0 ? absRadianDifference : 1.0
node.physicsBody?.applyTorque(SCNVector4Make(0, CGFloat(turnDirection.rawValue), 0, CGFloat(deltaTime * turnDampener)), impulse: true)
}