How to prevent initial rotation of a node when following a UIBezierPath?

Problem = I have a locomotive trying to follow an oval-shaped UIBezierPath. The challenge is that when I use orientToPath:true in my call to SKAction.follow, the locomotive immediately rotates even though the locomotive is initially positioned on the straight portion of the oval before it reaches the rounded portion of the oval.

If I could prevent this initial rotation, then the rotation would not happen until it reached the rounded portion of the oval - as it should.

So, can you please provide some guidance as to how I could prevent this initial rotation?

Here is the code:

   func createTrainPath() {
        
		    // this custom method ensures that the locomotive
        // moves CW, versus CCW
        trackPath = getBezierPath(theNode: myTrain, offset: 0.0)
                        
    }   // createTrainPath
    
    
    func startFollowTrainPath() {
        
        let theSpeed = Double(5*thisSpeed)
        var trainAction = SKAction.follow(
                                      trackPath.cgPath,
                                      asOffset: true,
                                      orientToPath: true,  // <==
                                      speed: theSpeed)
        trainAction = SKAction.repeatForever(trainAction)
        myTrain.run(trainAction, withKey: runTrainKey)

    }   // startFollowTrainPath

Replies

I don’t know if this is proper to say .. but I have been working on this rotation issue for a very long time and I need some serious help here.

Hello,

I believe you can achieve the functionality you are aiming for by using a "pivot" node. Effectively, you would add your "myTrain" node as a child of the "pivot". Then you would run the "trainAction" on the "pivot" instead of "myTrain".

This allows "myTrain" to maintain its local zRotation relative to the path, but still follow the path.

  • I am not ignoring you ... I'm just trying to absorb the mountain of information out there on "pivot".

    Right now, myTrain is a child of my master GameViewController.

    From what I have learned so far is that this "pivot" in my case is a created intermediate parent between myTrain and the above GameViewController.

    So, I let this pivot rotate to its heart's content, but its child = myTrain won't pivot because its zRotation = 0.

    Hopefully this is correct .. but I am not 100% certain ??

Add a Comment

From: Apple Developer Support ...

I believe you can achieve what you are aiming for by using a "pivot" node.

Effectively, you would add your "myTrain" node as a child of the "pivot".

Then you would run the "trainAction" on the "pivot" instead of "myTrain".

This allows "myTrain" to maintain its local zRotation relative to the path,

but still follow the path.

==

It’s seemingly funny how things work out .. I was very recently asked who my favorite college prof was and why.

I responded “Dr. Leno Pedrotti who taught Quantum Mechanics in Grad School. The reason I choose him as my favorite is because he was consistently able to reduce the extraordinarily convoluted subject to the simple, using simple and short statements in the process.”

The above writer from Apple Developer Support was made from the same mold as Dr. Pedrotti.

FWIW, we’re talking circa 1963 in case you “youngsters” are curious.

To continue, here is my code wherein I “attempted” to implement the above writer’s concise solution:

    func moveTrainForward() {

        // trackPath + myTrain = globals
        let forwardTrackPath = trackPath
        let trainAction = SKAction.follow(
                                      forwardTrackPath!.cgPath,
                                      asOffset: false,
                                      orientToPath: true,
                                      duration: 0.1)
        createPivotNode(forSpriteNode: myTrain)
        myTrain.run(trainAction)

    }   // moveTrainForward

And here is the key createPivotNode:

    func createPivotNode(forSpriteNode: SKSpriteNode) {
                
        let nodeGeometry = SCNBox(width: forSpriteNode.size.width,
                                  height: 1,
                                  length: forSpriteNode.size.height,
                                  chamferRadius: 0)
        let theSCNNode = SCNNode(geometry: nodeGeometry)
        // create "pivot" node
        theSCNNode.pivot = SCNMatrix4MakeTranslation(0.5, 0.5, 0.5)
                
        if let ourSceneNode = SCNScene(named: "GameScene") {
            
            // effectively add "myTrain" node as a child of the "pivot" ???
            ourSceneNode.rootNode.addChildNode(theSCNNode)

        }
                            
    }   // createPivotNode

Unfortunately, the above does not prevent the rotation mentioned in my OP.

I don't know for certain, but I think the core of this not working rests with my comment effectively add "myTrain" node as a child of the "pivot" ???

In conclusion, I need some further elaboration from my college prof.