I am getting lost in how nodespace coordinates and rotation are handled in scenekit.
How do I get the direction a node is facing after rotation so I can then apply force in that direction.
I assumed that a force along the -z axis applied to the node would always move it forward relative to the node however this is not the case.
Negative z is the "facing" direction of a node only in its own coordinate space (i.e. the one its children and geometry are positioned in). When you apply a force, you're working in the coordinate space containing the node.
So, if you want to apply a force in a node's forward direction, you'll need to convert a vector like {0,0,-1}
from the node's space to its parent's using a method like convertPosition:toNode:
.
I also couldn't get convertPosition:toNode
to work. Variations on ship.convertPosition(SCNVector3(0,0,-0.1), toNode: ship.parentNode)
made the node fly off at unpredictable directions and speeds.
What worked for me, was to grab the third row of the node's worldTransform
matrix, which corresponds to it's z-forward axis:
func getZForward(node: SCNNode) -> SCNVector3 {
return SCNVector3(node.worldTransform.m31, node.worldTransform.m32, node.worldTransform.m33)
}
ship.position += getZForward(ship) * speed
// if node has a physics body, you might need to use the presentationNode, eg:
// getZForward(ship.presentationNode)
// though you probably don't want to be directly modifying the position of a node with a physics body
// overrides for SCNVector arithmetic
#if os(iOS)
typealias VecFloat = Float
#elseif os (OSX)
typealias VecFloat = CGFloat
#endif
func + (lhs: SCNVector3, rhs: SCNVector3) -> SCNVector3 {
return SCNVector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z)
}
func * (lhs: SCNVector3, rhs: VecFloat) -> SCNVector3 {
return SCNVector3(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs)
}
func += (lhs: inout SCNVector3, rhs: SCNVector3) {
lhs = lhs + rhs
}
If the node has a physics body you might have to pass the node.presentationNode
into the function.
When iOS 11/ High Sierra come out, there'll be less need for overriding SCNVector3, because all the SCNNode
properties have simd
equivalents, so as well as .position
there's .simdPosition
and so on, and there are a lot of common simd operations built in.
iOS 11 update
iOS 11 adds handy convenience functions for getting the orientation of a node. In this case the worldForward
property is the one you want. Also, all of the properties on SCNNode
that return SCNVector
and matrix types now have versions that return simd types. Because simd already has overloads for the arithmetic operators, you no longer need to add sets of arithmetic overrides for the SCNVector
and SCNMatrix
types.
So we can get rid of out getZForward
method above, and just have the line:
ship.simdPosition += ship.simdWorldFront * speed
The other handy set of methods that iOS 11 adds, are a set of convertVector
methods, to complement the existing convertPosition
methods. convertVector
is the equivalent of multiplying the matrix by the vector with 0
in the w
position, so that the translation of the matrix is ignored. These are the appropriate methods to use for converting things like normals, directions and so on from one node's space to another.
Because the accepted answer uses convertPosition
, I believe it will only produce correct results for nodes whose translation is the 0,0,0 origin