如何获取并控制通过SCNScene加载到SCNView的USDZ模型自带动画?
这个问题我之前处理过不少次——USDZ模型的动画经常藏在你没注意的地方,不是直接挂在节点的animationKeys里的。让我一步步帮你解决:
第一步:改用SCNSceneSource加载,禁用自动播放
你直接用SCNScene(url:)加载模型时,场景默认会自动播放自带的动画,而且这些动画可能并没有直接附加到节点上,而是存在于SCNSceneSource的资源容器里,这就是为什么你遍历所有节点都找不到animationKeys的原因。
先换一种加载方式,用SCNSceneSource来获取所有资源,同时关闭自动播放的默认行为:
guard let sceneSource = SCNSceneSource(url: url, options: [ SCNSceneSource.LoadingOption.checkConsistency: true, // 关键配置:阻止场景自动播放所有内置动画 SCNSceneSource.LoadingOption.playAutomatically: false ]) else { fatalError("Failed to load scene source from URL") } // 从sceneSource中加载场景实例 guard let scene = sceneSource.scene() else { fatalError("Failed to retrieve scene from source") } sceneView.scene = scene sceneView.scene?.isPaused = true
第二步:获取模型自带的所有动画
通过sceneSource的animationIdentifiers属性,你能拿到所有内置动画的唯一标识:
let animationIDs = sceneSource.animationIdentifiers print("Found \(animationIDs.count) animations: \(animationIDs)")
这些ID就是你后续控制动画的核心依据。
第三步:绑定动画到节点并控制播放
接下来,你可以通过ID获取对应的SCNAnimationPlayer(这是控制动画播放/暂停/停止的核心对象),然后把它绑定到目标节点上——如果模型的动画有指定目标节点,你可以通过节点名称精准查找;如果不确定,绑定到根节点也能生效:
for animationID in animationIDs { // 获取对应动画的播放器 guard let animationPlayer = sceneSource.player(forAnimationIdentifier: animationID) else { print("Failed to load player for animation \(animationID)") continue } // 示例:绑定到根节点,你可以替换成具体子节点的名称 let targetNode = scene.rootNode // 将播放器绑定到节点,用动画ID作为key targetNode.addAnimationPlayer(animationPlayer, forKey: animationID) // 现在你可以直接控制这个播放器: // animationPlayer.play() // 启动播放 // animationPlayer.pause() // 暂停动画 // animationPlayer.stop() // 停止并重置动画 }
第四步:后续随时控制动画
之后如果需要调整某个动画状态,只需要通过节点和对应的key获取播放器即可:
// 暂停指定动画 if let player = scene.rootNode.animationPlayer(forKey: "YourAnimationID") { player.pause() } // 重新启动播放 player.play() // 彻底移除动画(停止+清除资源) scene.rootNode.removeAnimation(forKey: "YourAnimationID")
为什么之前找不到动画?
USDZ格式的动画通常是作为独立资源存储在场景源中的,而不是直接附加到节点上。直接用SCNScene(url:)加载时,系统会自动帮你把动画绑定到节点并播放,但不会把这些动画的key暴露在节点的animationKeys里——只有当你手动添加动画播放器时,animationKeys才会显示对应的key。
这样处理后,你就能完全掌控模型的所有动画了,再也不会出现不受控制的自动循环了。
内容的提问来源于stack exchange,提问作者Praveen Kumar

