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
Post a Comment