swift - Use PhysicsBody to rotate an SCNNode to match the Thumbstick's radian -


i'm working on top-down space game built using swift , scenekit following setup:

scnnode representing spaceship

  • rotation constrained y axis; values range -m_pi_2 m_pi + m_pi_2
  • movement constrained x , z axes.

game controller thumbstick input

  • values range -1.0 1.0 on x , y axes.

when game controller's thumbstick changes position, spaceship should rotate using physics body match thumbstick's radian.

the target radian of thumbstick can calculated following:

let targetradian = m_pi_2 + atan2(-y, -x) 

the current radian of node can obtained following:

let currentradian = node.presentationnode.rotation.w * node.presentationnode.rotation.y 

nstimeinterval deltatime provides time in seconds since last rotation calculation.

how can node rotated using angularvelocity, applytorque, or physics method reach targetradian?

the difference between targetradian , currentradian ranged 0.0 -2π depending on value of currentradian. equation determine shortest direction turn, .clockwise or .counterclockwise, reach targetradian:

let turndirection = (radiandifference + (m_pi * 2)) % (m_pi * 2) < m_pi ? rotationdirection.counterclockwise : rotationdirection.clockwise 

using applytorque, there possibility over-rotate past targetradian resulting in wobbling effect, compass magnetizing toward point, rotation changes direction , forth reach targetradian. following, while not perfect solution, dampened effect:

let turndampener = abs(radiandifference) < 1.0 ? abs(radiandifference) : 1.0 

the complete solution thus:

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) } 

Comments