简体   繁体   中英

Node group's position is reset at the start of SCNNode.runAction

I have some code that rotates several SCNNodes around the x axis when the screen is tapped like so:

func handleTap(gestureRecognize: UIGestureRecognizer) {
    let sceneView = self.view as SCNView

    let slice = self.cubes[0...8]
    let container = SCNNode()
    for node: SCNNode in slice {
        container.addChildNode(node)
    }
    sceneView.scene!.rootNode.addChildNode(container)
    container.runAction(SCNAction.rotateByX(CGFloat(M_PI / 2), y: 0.0, z: 0.0, duration: 1), completionHandler: { () -> Void in
        println("complete")
    })
}

The issue that I'm running into is that every time this function is called, the nodes seem to reset themselves to their original position before performing the action. When the action is complete, they appear to stay in the correct place until the screen is tapped again. How do I make subsequent calls to handleTap rotate them from their current position?

I've tried removing the nodes from their original parent before adding them to the container, but it has no visible effect.

I've also tried using an animation

    let spin = CABasicAnimation(keyPath: "rotation")
    spin.fromValue = NSValue(SCNVector4: SCNVector4(x: -1, y: 0, z: 0, w: 0))
    spin.toValue = NSValue(SCNVector4: SCNVector4(x: -1, y: 0, z: 0, w: Float(M_PI_2)))
    spin.duration = 3
    spin.repeatCount = .infinity
    container.addAnimation(spin, forKey: "spin around")

Which had the exact same effect as the action.

If I put the nodes back as children of the root view in the complete block of the runAction

    container.runAction(action, completionHandler: { () -> Void in
        println("completed rotation")
        for node: SCNNode in slice {
            node.removeFromParentNode()
            sceneView.scene!.rootNode.addChildNode(node)
        }
    })

Then the nodes are returned to their original position on completion of the action, rather than at the beginning of a new tap.

When the node is removed from the scene's root node, added to the container and rotated, a tranform is applied to the node relative to the container's local coordinate system. The solution was to convert the node's transform from the container's coordinate system to the root node's coordinate system before adding it back to the root node.

func handleTap(gestureRecognize: UIGestureRecognizer) {
    let sceneView = self.view as SCNView
    let action = SCNAction.rotateByAngle(CGFloat(M_PI_2), aroundAxis: SCNVector3Make(-1, 0, 0), duration: 1)
    let slice = self.cubes[0...8]
    let container = SCNNode()
    let root = sceneView.scene!.rootNode


    for node: SCNNode in slice {
        container.addChildNode(node)
    }

    root.addChildNode(container)

    container.runAction(action, completionHandler: { () -> Void in
        for node: SCNNode in slice {
            let transform = node.parentNode!.convertTransform(node.transform, toNode: root)
            node.removeFromParentNode()
            node.transform = transform
            root.addChildNode(node)
        }
    })
}

This is how I rotate a SCNNode without a container or anything like that:

let targetRotationRadians: CGFloat = targetRotationAngle * (CGFloat.pi / 180.0)
let rotationAction = SCNAction.rotateTo(x: 0.0, y: targetRotationRadians, z: 0.0, duration: animationDuration, usesShortestUnitArc: true)
yourSCNNode.runAction(rotationAction)

The important thing is setting usesShortestUnitArc to true in for the SCNAction.rotateTo method. That way, the node doesn't have to return to its original angle before rotating to the desired angle.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM