Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding and transitioning animations in SceneKit

I looked at Banana game from WWDC which is written in Objective-C was trying to convert the code to Swift for importing animation and transitioning between them but I am having problems running the animations in swift from DAE files.

I have exporter DAE files in both AutoDesk format from 3dsMax and in openCollada format. The Autodesk format the animation is for each bone so I am unable to call an animation by name so I just import the scene and do the following for the animation to start as soon as the file loads.

scene = SCNScene(named: "monster.scnassets/monsterScene.DAE") 
scene2 = SCNScene(named:"monster.scnassets/monster.DAE")

var heroNode = SCNNode()
heroNode = scene.rootNode.childNodeWithName("heroNode", recursively: false)!

var nodeArray = scene2.rootNode.childNodes

for childNode in nodeArray {
    heroNode.addChildNode(childNode as SCNNode)
}

Although the animation plays as soon as the scene starts I done know how to store the animation.

If I export the collada file using openCollada. I can use the following to run the get and run the animation as there is just one animation for the whole object instead of each bone in case of AutoDesk collada format. By this way I can store the animation also using CAAnimation.

var anim = scene2.rootNode.animationForKey("monster-1")
childNode.addAnimation(anim, forKey: "monster-1")  

But then the character runs at an angle and also runs back and forth instead of running at the same spot.

Also the lighting is better using openCollada. I just would like to use openCollada instead of autodesk collada export. Right now I am using openCollada format for exporting the scene and autodesk for exporting character.

How do store animations in SceneKit/Swift and transition between them? Thanks.

like image 587
Siddharth Shekar Avatar asked Feb 04 '15 06:02

Siddharth Shekar


1 Answers

If you don't change the default options when loading a scene from a file, all animations in the scene immediately and automatically attach to their target nodes and play. (See the Animation Import Options in the SceneKit API reference.)

If you want to load the animations from a scene file and hold on to them for attaching to nodes (that is, playing) later, you're better off loading them with the SCNSceneSource class. In addition, you can (but don't have to) store your base model in one file and animations in other files.


Just look at this Bananas animation loading code. Just look at it.*

// In AAPLGameLevel.m:
SCNNode *monkeyNode = [AAPLGameSimulation loadNodeWithName:nil fromSceneNamed:@"art.scnassets/characters/monkey/monkey_skinned.dae"];
AAPLMonkeyCharacter *monkey = [[AAPLMonkeyCharacter alloc] initWithNode:monkeyNode];
[monkey createAnimations];

// In AAPLSkinnedCharacter.m (parent class of AAPLMonkeyCharacter):
+ (CAAnimation *)loadAnimationNamed:(NSString *)animationName fromSceneNamed:(NSString *)sceneName
{
    NSURL *url = [[NSBundle mainBundle] URLForResource:sceneName withExtension:@"dae"];
    SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:url options:nil ];
    CAAnimation *animation = [sceneSource entryWithIdentifier:animationName withClass:[CAAnimation class]];
    //...
}

// In AAPLMonkeyCharacter.m:
- (void)update:(NSTimeInterval)deltaTime
{
    // bunch of stuff to decide whether/when to play animation, then...
    [self.mainSkeleton addAnimation:[self cachedAnimationForKey:@"monkey_get_coconut-1"] forKey:nil];
    //...
}

What's going on here:

  1. There's a custom class managing the animated character. It owns a SCNNode containing the character model, as well as a bunch of CAAnimations for all of the things the model can do (idle/jump/throw/etc).
  2. That class is initialized by passing the character node loaded from one DAE file. (AAPLGameSimulation loadNodeWithName:fromSceneNamed: is a convenience wrapper around loading a SCNScene from a file and grabbing a named node out of it.) That DAE file contains only the character model, with no animations.
  3. Then, AAPLMonkeyCharacter loads (and stores references to) the animations it needs from the separate DAE files containing each animation. This is where SCNSceneSource comes in — it lets you grab animations out of the file without playing them.
  4. When it's time to play, the monkey class calls addAnimation:forKey: to run the animation on its main node.

Translating to Swift and applying to your problem — where you seem to have all animations in the same file — I'd do something like this (vague outline of a hypothetical class):

class Monster {
    let node: SCNNode
    let attackAnimation: CAAnimation

    init() {
        let url = NSBundle.mainBundle().URLForResource(/* dae file */)
        let sceneSource = SCNSceneSource(URL: url, options: [
            SCNSceneSourceAnimationImportPolicyKey : SCNSceneSourceAnimationImportPolicyDoNotPlay
        ])
        node = sceneSource.entryWithIdentifier("monster", withClass: SCNNode.self)
        attackAnimation = sceneSource.entryWithIdentifier("monsterIdle", withClass: CAAnimation.self)
    }

    func playAttackAnimation() {
        node.addAnimation(attackAnimation, forKey: "attack")
    }
}

The key bits:

  • SCNSceneSourceAnimationImportPolicyDoNotPlay makes sure that nodes loaded from the scene source don't start with animations attached/playing.
  • You have to load the animations separately with entryWithIdentifier:withClass:. Be sure to configure them how you like (repeating, fade duration etc) before attaching to the nodes.
like image 195
rickster Avatar answered Oct 25 '22 01:10

rickster