在 SceneKit 中添加和转换动画
Adding and transitioning animations in SceneKit
我看了 WWDC 的 Banana 游戏,它是在 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")
但是角色 运行 有一个角度并且 运行 来回而不是 运行 在同一个位置。
此外,使用 openCollada 时灯光效果更好。我只想使用 openCollada 而不是 autodesk collada export。现在我正在使用 openCollada 格式导出场景和 autodesk 导出角色。
如何在 SceneKit
/Swift
中存储动画并在它们之间进行转换?谢谢。
如果从文件加载场景时不更改默认选项,场景中的所有动画会立即自动附加到它们的目标节点并播放。 (请参阅 SceneKit API 参考中的 Animation Import Options。)
如果你想从场景文件加载动画并保留它们以便稍后附加到节点(即播放),你最好使用 SCNSceneSource
class.此外,您可以(但不是必须)将基础模型存储在一个文件中,将动画存储在其他文件中。
看看这个香蕉动画加载代码。看看就行了*
// 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];
//...
}
这里发生了什么:
- 有一个自定义 class 管理动画角色。它拥有一个包含角色模型的
SCNNode
,以及一堆 CAAnimation
用于模型可以做的所有事情 (idle/jump/throw/etc)。
- 那class是通过传递从一个DAE文件加载的字符节点来初始化的。 (
AAPLGameSimulation loadNodeWithName:fromSceneNamed:
是一个方便的包装器,围绕从文件加载 SCNScene
并从中获取命名节点。)该 DAE 文件仅包含角色模型,没有动画。
- 然后,
AAPLMonkeyCharacter
从包含每个动画的单独 DAE 文件加载(并存储引用)所需的动画。这就是 SCNSceneSource
的用武之地——它可以让您从文件中抓取动画而不播放它们。
- 当开始播放时,猴子 class 调用
addAnimation:forKey:
到 运行 其主节点上的动画。
翻译成 Swift 并应用于您的问题 — 您似乎将所有动画都放在同一个文件中 — 我会做这样的事情(假设 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")
}
}
关键位:
SCNSceneSourceAnimationImportPolicyDoNotPlay
确保从场景源加载的节点不以动画开头 attached/playing.
- 您必须使用
entryWithIdentifier:withClass:
单独加载动画。在附加到节点之前,请务必按照您的喜好配置它们(重复、淡入淡出持续时间等)。
我看了 WWDC 的 Banana 游戏,它是在 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")
但是角色 运行 有一个角度并且 运行 来回而不是 运行 在同一个位置。
此外,使用 openCollada 时灯光效果更好。我只想使用 openCollada 而不是 autodesk collada export。现在我正在使用 openCollada 格式导出场景和 autodesk 导出角色。
如何在 SceneKit
/Swift
中存储动画并在它们之间进行转换?谢谢。
如果从文件加载场景时不更改默认选项,场景中的所有动画会立即自动附加到它们的目标节点并播放。 (请参阅 SceneKit API 参考中的 Animation Import Options。)
如果你想从场景文件加载动画并保留它们以便稍后附加到节点(即播放),你最好使用 SCNSceneSource
class.此外,您可以(但不是必须)将基础模型存储在一个文件中,将动画存储在其他文件中。
看看这个香蕉动画加载代码。看看就行了*
// 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];
//...
}
这里发生了什么:
- 有一个自定义 class 管理动画角色。它拥有一个包含角色模型的
SCNNode
,以及一堆CAAnimation
用于模型可以做的所有事情 (idle/jump/throw/etc)。 - 那class是通过传递从一个DAE文件加载的字符节点来初始化的。 (
AAPLGameSimulation loadNodeWithName:fromSceneNamed:
是一个方便的包装器,围绕从文件加载SCNScene
并从中获取命名节点。)该 DAE 文件仅包含角色模型,没有动画。 - 然后,
AAPLMonkeyCharacter
从包含每个动画的单独 DAE 文件加载(并存储引用)所需的动画。这就是SCNSceneSource
的用武之地——它可以让您从文件中抓取动画而不播放它们。 - 当开始播放时,猴子 class 调用
addAnimation:forKey:
到 运行 其主节点上的动画。
翻译成 Swift 并应用于您的问题 — 您似乎将所有动画都放在同一个文件中 — 我会做这样的事情(假设 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")
}
}
关键位:
SCNSceneSourceAnimationImportPolicyDoNotPlay
确保从场景源加载的节点不以动画开头 attached/playing.- 您必须使用
entryWithIdentifier:withClass:
单独加载动画。在附加到节点之前,请务必按照您的喜好配置它们(重复、淡入淡出持续时间等)。