在SceneKit中添加和转换动画

时间:2015-02-04 06:49:16

标签: swift animation scenekit

我看过来自WWDC的Banana游戏,用Objective-C编写试图将代码转换为Swift以导入动画并在它们之间转换,但是我在从DAE快速运行动画时遇到问题文件。

我有来自3dsMax和openCollada格式的AutoDesk格式的导出器DAE文件。 Autodesk格式化动画是针对每个骨骼的,因此我无法按名称调用动画,因此我只需导入场景并执行以下操作即可在文件加载后立即启动动画。

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)
}

尽管动画在场景开始后立即播放,但我知道如何存储动画。

如果我使用openCollada导出collada文件。我可以使用以下命令来运行get并运行动画,因为在AutoDesk collada格式的情况下,整个对象只有一个动画而不是每个骨骼。通过这种方式,我也可以使用CAAnimation存储动画。

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

然后角色以某个角度运行并且来回奔跑而不是在同一个地方跑。

使用openCollada也可以更好地照明。我只想使用openCollada而不是autodesk collada export。现在我使用openCollada格式导出场景,使用autodesk导出字符。

如何在SceneKit / Swift中存储动画并在它们之间进行转换?感谢。

1 个答案:

答案 0 :(得分:13)

如果在从文件加载场景时不更改默认选项,则场景中的所有动画会立即自动附加到目标节点并播放。 (请参阅SceneKit API参考中的Animation Import Options。)

如果要从场景文件中加载动画并保留它们以便稍后附加到节点(即播放),最好使用SCNSceneSource类加载它们。此外,您可以(但不必)将基本模型存储在一个文件中,将动画存储在其他文件中。


看看这个Bananas动画加载代码。看看吧。*

// 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];
    //...
}

这里发生了什么:

  1. 有一个管理动画角色的自定义类。它拥有包含角色模型的SCNNode,以及模型可以执行的所有事情的一堆CAAnimation(空闲/跳转/抛出/等)。
  2. 通过传递从一个DAE文件加载的字符节点来初始化该类。 (AAPLGameSimulation loadNodeWithName:fromSceneNamed:是从文件加载SCNScene并从中获取命名节点的便利包装。)该DAE文件仅包含字符模型,没有动画。
  3. 然后,AAPLMonkeyCharacter从包含每个动画的单独DAE文件中加载(并存储对其所需动画的引用)。这是SCNSceneSource的用武之地 - 它可以让您从文件中抓取动画而无需播放它们。
  4. 当玩的时候,猴子类调用addAnimation:forKey:在主节点上运行动画。

  5. 转换为Swift并应用于您的问题 - 您似乎在同一个文件中拥有所有动画 - 我会做这样的事情(假设类的模糊轮廓):

    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")
        }
    }
    

    关键位:

    • SCNSceneSourceAnimationImportPolicyDoNotPlay确保从场景源加载的节点不会以附加/播放的动画开始。
    • 您必须使用entryWithIdentifier:withClass:单独加载动画。在连接到节点之前,请务必配置它们的方式(重复,淡入淡出持续时间等)。