'SceneKit: How to constrain movement of a node to one axis locally, not the entire world

It's very easy to prevent movement of a node in one axis (X, Y, or Z) being moved by other forces with an SCNTransformConstraint.positionConstraint in world space as follows:

let zPosition = node.position.z

let strafingConstraint = SCNTransformConstraint.positionConstraint(inWorldSpace: true, with: {(_ node: SCNNode, _ position: SCNVector3) -> SCNVector3 in
         localPosition = position
         localPosition.z = zPosition
         return localPosition
})

node.constraints = [strafingConstraint];

However, this constrains this in world space. What do I do if I want to just make the node no longer move in its local Z axis, regardless of its orientation, so it can only move in its local X and Y axes, what do I do? Is there a way to use the .convertPosition(:from:) or .convertPosition(:to:) functions to do this? Is there a better way?



Solution 1:[1]

This might be an alternative approach to fix a node to a specific axis without using a SCNConstraint.

Use the updateAtTime delegate function to adjust the z-position of your node within each frame:

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
    node.position.z = 0.0 // or wherever you want to have it
}

(If your node has a physics body attached to it, you should only alter it's position when the body type is = .kinematc)

Solution 2:[2]

With this constraint myNode only moves along the x and y axes and is constrained to zPosition along the z axis:

let zAxisConstraint = SCNTransformConstraint.positionConstraint(inWorldSpace: true) { node, position
    in
    let newPosition = SCNVector3(position.x, position.y, zPosition)
    return newPosition
}

myNode.constraints = [zAxisConstraint]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ZAY
Solution 2 Paul Broderick