Getting direction that SCNNode is facing

2019-05-14 09:48发布

问题:

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.

回答1:

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:.



回答2:

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