Pausing a SceneKit animation

2019-05-11 16:44发布

问题:

I'm trying to create a test app in which the user can pause an animation by clicking in the SceneView. The SceneView loads the animation from a .dae file created in a 3d app (Cinema 4D). The app successfully plays and loops the animation upon launch.

To pause the animation, I used Technical Q&A QA1673 as a reference. In the case of this .dae file, the animation actually comes in as a hierarchy of animations, so I have tried reaching down to each underlying CAKeyframeAnimation and setting its speed to zero. My code currently looks like this:

- (void)mouseDown:(NSEvent *)event {

     SCNNode *cubeNode = [self.scene.rootNode childNodeWithName:@"C4D_Cube" recursively:YES];
     CAAnimation *cubeAnimation = [cubeNode animationForKey:@"Cube_Anim_01-02-1"];      
     CAAnimationGroup *cubeAnimationGroup = (CAAnimationGroup *)cubeAnimation;

     // cubeAnimationGroup contains 3 CAAnimationGroups, each of which contains a CAKeyframeAnimation.
     // So I directly access each CAKeyframeAnimation and set its speed to zero.
     for (CAAnimationGroup *subGroup in [cubeAnimationGroup animations]) {
          CFTimeInterval pausedTime = CACurrentMediaTime();
          [[subGroup animations] setValue:@0.0 forKey:@"speed"];
          [[subGroup animations] setValue:[NSNumber numberWithFloat:pausedTime] forKey:@"timeOffset"];
     }
}

When I set a breakpoint, I can see that the speed of the keyframe animations does change from 1 to 0, but the animation continues to play at its normal speed in the scene view. I originally tried just setting the speed on the top level CAAnimationGroup to zero, but this also had no effect. What's the correct way to pause an animation in progress?

回答1:

The animations returned by "animationForKey:" are copies of the running animations. The documentation says "Attempting to modify any properties of the returned object will result in undefined behavior." So you could do something like this instead:

for(NSString *key in [myNode animationKeys]){
    CAAnimation *animation = [myNode animationForKey:key];
    [animation setSpeed:0]; //freeze
    [animation setTimeOffset:CACurrentMediaTime() - [animation beginTime]]; //move back in time
    [cube addAnimation:animation forKey:key]; //re-add the animation with the same key to replace
}

Note that if you just want to pause all the animations coming from a .DAE you might want to do:

[mySCNView setPlaying:NO]; //pause scene-time based animations



回答2:

Or you could set paused to true.

In Swift:

mySCNView.scene?.paused = true