swift 在SceneKit中添加和过渡动画

wfypjpf4  于 2023-01-16  发布在  Swift
关注(0)|答案(2)|浏览(209)

我看了WWDC中的香蕉游戏,它是用Objective-C编写的,我试图将代码转换为Swift,以便导入动画并在它们之间转换,但我在从DAE文件运行Swift中的动画时遇到了问题。
我有来自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")

但是之后角色以一个Angular 跑,也来回跑,而不是在同一个点跑。
使用openCollada的灯光效果也更好。我只是想使用openCollada而不是autodesk collada导出。现在我使用openCollada格式导出场景,autodesk导出角色。
如何在SceneKit/Swift中存储动画并在它们之间转换?谢谢。

qnakjoqk

qnakjoqk1#

如果在从文件加载场景时不更改默认选项,则场景中的所有动画将立即自动附加到其目标节点并播放。(请参见SceneKit API参考中的动画导入选项。)
如果你想从一个场景文件中加载动画,并保留它们以便以后附加到节点(即播放),你最好用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,以及一堆CAAnimation,用于模型可以做的所有事情(idle/jump/throw等)。
1.通过传递从一个DAE文件加载的角色节点来初始化该类。(AAPLGameSimulation loadNodeWithName:fromSceneNamed:是一个方便的 Package 器,它可以从文件加载SCNScene并从中获取命名节点。)该DAE文件只包含角色模型,没有动画。
1.然后,AAPLMonkeyCharacter从包含每个动画的单独DAE文件中加载所需的动画(并存储对这些动画的引用)。这就是SCNSceneSource的用武之地-它允许您从文件中抓取动画,而无需播放它们。
1.播放时,monkey类调用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:单独加载动画。确保在连接到节点之前按照你喜欢的方式配置它们(重复,淡入淡出持续时间等)。
hc2pp10m

hc2pp10m2#

2022代码片段。

就@rickster回答中的swift样本而言。
看起来代码现在更像是:

let p = Bundle.main.url(forResource: "File Name", withExtension: "dae")!
source = SCNSceneSource(url: p, options: nil)!

let geom = source.entryWithIdentifier("geometry316",
   withClass: SCNGeometry.self)!
   as SCNGeometry
yourDragonNode = SCNNode(geometry: geom)

yourAnime = source.entryWithIdentifier("unnamed_animation__0",
   withClass: CAAnimation.self)!

在如何获得“神秘字符串”方面:
“几何图形316”和“未命名动画__0”。
注意:https://stackoverflow.com/a/75088130/294884
和:https://stackoverflow.com/a/56787980/294884

相关问题