Bbx
Bbx

Reputation: 3394

In SceneKit SCNAction hangs when called from completion handler of RunAction

Calling an SCNAction from the completion handler of RunAction seems to hang SceneKit.

A touch event or rotating the device seems to unblock the hang.

To reproduce:

1) Take the default SceneKit project you get on startup with the rotating spaceship.

2) Replace the animation code:

ship.RunAction(SCNAction.RepeatActionForever(SCNAction.RotateBy(0, 2, 0, 1)));

with:

        ship.RunAction(SCNAction.RotateBy(0, 2, 0, durationInSeconds: 3.0f), delegate
        {
            Console.WriteLine("DONE ROTATE");
            ship.RunAction(SCNAction.MoveBy(1, 0, 0, durationInSeconds: 3.0f), delegate
            {
                Console.WriteLine("DONE MOVEBY");
            });
        });

3) Run on the simulator or real device (the problem is the same on both)

4) The results is:

I'm using C# and Visual Studio for Mac, but I suspect it happens using Xcode too.

Is this a bug in SceneKit? How can a workaround be done?

Maybe this is the same issue as described here:

SCNAction completion handler awaits gesture to execute

Upvotes: 0

Views: 887

Answers (3)

Tony
Tony

Reputation: 1611

My solution is to use DispatchQueue to lunch inner actions:

piece.runAction(action0, completionHandler: {
    DispatchQueue.main.asyncAfter(deadline: .now()) {
       ship.runAction(action1)
    }
}

Upvotes: 0

Bbx
Bbx

Reputation: 3394

Using SCNTransaction with a completionBlock does not suffer from the same problem, so this works fine:

        SCNTransaction.Begin();
        SCNTransaction.AnimationDuration = 3.0f;
        SCNTransaction.SetCompletionBlock(() =>
        {
            Console.WriteLine("DONE ROTATE");
            SCNTransaction.Begin();
            SCNTransaction.AnimationDuration = 3.0f;
            SCNTransaction.SetCompletionBlock(() =>
            {
                Console.WriteLine("DONE MOVEBY");
            });
            ship.Position = new SCNVector3(1.0f, 0.0f, 0.0f);
            SCNTransaction.Commit();
        });
        ship.EulerAngles = new SCNVector3(0.0f, (float)Math.PI / 2, 0.0f);
        SCNTransaction.Commit();

(Also using CABasicAnimation with CAAnimationDelegate to do the callback works OK.)

Since SCNTransaction and CABasicAnimation work, but RunAction doesn't, it really looks like an Apple bug in RunAction.

Upvotes: 1

MasDennis
MasDennis

Reputation: 746

This happens because by default SceneKit is not rendering continuously. When tapping the screen the scene is changed and a new frame will be rendered. That's why the moveBy action is not triggered immediately after the rotateBy action.

Try setting SCNView's renderContinuously property to true like so:

scnView.rendersContinuously = true
ship.runAction(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 3.0)) {
    print("DONE ROTATE")
    ship.runAction(SCNAction.moveBy(x: 1, y: 0, z: 0, duration: 3.0), completionHandler: {
        print("DONE MOVEBY")
        scnView.rendersContinuously = false
    })
}

Upvotes: 3

Related Questions