【问题标题】:Adding and transitioning animations in SceneKit在 SceneKit 中添加和过渡动画
【发布时间】:2015-04-03 14:33:16
【问题描述】:

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

我有来自 3dsMax 的 AutoDesk 格式和 openCollada 格式的导出器 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 文件。我可以使用以下命令运行获取并运行动画,因为在 AutoDesk collada 格式的情况下,整个对象而不是每个骨骼只有一个动画。通过这种方式,我也可以使用CAAnimation 来存储动画。

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

但随后角色以一个角度跑,并且来回跑,而不是在同一个地方跑。

此外,使用 openCollada 照明效果更好。我只想使用 openCollada 而不是 Autodesk collada 导出。现在我使用 openCollada 格式导出场景,使用 Autodesk 导出角色。

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

【问题讨论】:

    标签: swift animation scenekit


    【解决方案1】:

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

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


    看看这个香蕉动画加载代码。看看就好*

    // 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,以及一组用于模型可以做的所有事情(空闲/跳跃/投掷/等)的CAAnimations。
    2. 通过传递从一个 DAE 文件加载的字符节点来初始化该类。 (AAPLGameSimulation loadNodeWithName:fromSceneNamed: 是从文件中加载 SCNScene 并从中获取命名节点的便捷包装器。)该 DAE 文件仅包含角色模型,没有动画。
    3. 然后,AAPLMonkeyCharacter 从包含每个动画的单独 DAE 文件加载(并存储对)它所需的动画。这就是SCNSceneSource 的用武之地——它可以让您从文件中抓取动画而不播放它们。
    4. 播放时,猴子类调用addAnimation:forKey: 在其主节点上运行动画。

    翻译成 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: 单独加载动画。在附加到节点之前,请务必按照您喜欢的方式配置它们(重复、淡入淡出持续时间等)。

    【讨论】:

    • 谢谢,我会检查一下。 :D。 sry 甚至不能赞成你的答案。需要 15 个代表。
    猜你喜欢
    • 1970-01-01
    • 2016-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    • 2016-02-03
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多