使用PhysicsBody旋转SCNNode以匹配Thumbstick的弧度

时间:2016-01-29 04:05:40

标签: swift game-physics scenekit radians scnnode

我正在使用Swift和SceneKit构建自上而下的太空游戏,并进行以下设置:

代表太空船的SCNNode

  • 旋转受约束于y轴;值范围从-M_PI_2M_PI + M_PI_2
  • 运动受限于x和z轴。

游戏控制器缩略图输入

  • x和y轴上的值范围从-1.01.0

当游戏控制器的拇指操纵杆改变位置时,宇宙飞船应使用物理体旋转以匹配拇指操纵杆的弧度。

可以使用以下方法计算拇指操纵杆的目标弧度:

let targetRadian = M_PI_2 + atan2(-y, -x)

可以通过以下方式获得节点的当前弧度:

let currentRadian = node.presentationNode.rotation.w * node.presentationNode.rotation.y

NSTimeInterval deltaTime提供自上次轮换计算以来的秒数。

如何使用angularVelocityapplyTorque或其他物理方法旋转节点以到达targetRadian

1 个答案:

答案 0 :(得分:0)

targetRadiancurrentRadian之间的差异范围从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)
}