SceneKit - 停止连续循环 Collada 动画

Posted

技术标签:

【中文标题】SceneKit - 停止连续循环 Collada 动画【英文标题】:SceneKit - Stop continuously looping Collada animation 【发布时间】:2015-06-23 21:39:47 【问题描述】:

我正在使用 SceneKit 加载 Collada (.dae) 文件。

无论我尝试过什么,动画都会连续循环重复,而我只希望它播放一次然后停止。

这里是加载代码,其中 sceneView 是我的 SCNView:

    //Load the scene and play
    let scene = SCNScene(named: "Scenes.scnassets/test4.dae")
    sceneView.scene = scene
    sceneView.autoenablesDefaultLighting = true
    sceneView.allowsCameraControl = true

场景正确加载,动画在加载时按要求播放,但在连续循环中。

我已尝试删除所有动画,如下所示:

sceneView.scene?.rootNode.removeAllAnimations()

但这似乎没有效果。

我也尝试检索所有动画并将 repeatCount = 1 甚至设置为 0

    let url = NSBundle.mainBundle().URLForResource("Scenes.scnassets/test4", withExtension: "dae")
    let sceneSource = SCNSceneSource(URL:url!, options: nil)


    let animationIndentifiers = sceneSource?.identifiersOfEntriesWithClass(CAAnimation)

    if let ids = animationIndentifiers
    
        for id in ids 
            let animation: CAAnimation = sceneSource!.entryWithIdentifier(id as! String, withClass: CAAnimation.self)! as! CAAnimation
            animation.repeatCount = 1
        
    

我也尝试过另一种方式:

let keys = sceneView.scene!.rootNode.animationKeys() //获取所有动画键

if let theKeys = keys 
    for key in theKeys 
        let animation = sceneView.scene?.rootNode.animationForKey(key as! String)
        animation?.repeatCount = 1
    

但还是没有运气。

我在 Collada 文件本身中找不到任何明显导致动画重复的内容。但是,我注意到我的 Mac 磁盘上的 .dae 文件图标有一个播放按钮,单击此按钮还会连续循环播放图标内的动画。

更新:

我现在注意到,在上面的代码中,我设置了常量“动画”属性,而这并没有复制回实际的场景节点。此外,.dae 文件中的唯一动画位于子节点中。所以这是我的下一个尝试:

  //Get the child nodes
  let children = scene?.rootNode.childNodes

  //Initialise a childNode counter
  var childCount = 0

  //Iterate through the child nodes     
  if let theChildren = children 
     for child in theChildren 
        let keys = child.animationKeys()  //get all the animation keys
        //Iterate through the animations
        if let theKeys = keys 
           for key in theKeys 
               let animation = child.animationForKey(key as! String)
               println("Initial Repeat count: \(animation.repeatCount)")
               animation.repeatCount = 1
               //Remove existing animation
               scene?.rootNode.childNodes[childCount].removeAnimationForKey(key as! String)
               //Add amended animation
               scene?.rootNode.childNodes[childCount].addAnimation(animation, forKey: key as! String)
            
         
         childCount++
    
 

实际上只有一个动画附加到一个子节点。 (我也尝试使用实际的动画 id 字符串代替上面的 'key' 来设置它。)

以上显示Initial Repeat count: inf 之后检查它确实设置为1。 但是,动画仍然在无限循环中运行:-(

我们将不胜感激任何解决此问题的帮助。

进一步更新

我现在使用 Maya 创建了一个带有简单动画的新 Collada 文件,出于某种原因,上面尝试的其中一个试验确实有效:

    func sceneSetup() 
      let scene = SCNScene(named: "Scenes.scnassets/test10.dae")

      let children = scene?.rootNode.childNodes
      var childCount = 0
      if let theChildren = children 
        for child in theChildren 
          let keys = child.animationKeys()  //get all the animation keys
          if let theKeys = keys 
            for key in theKeys 
              let animation = child.animationForKey(key as! String)
              animation.repeatCount = 1
              scene?.rootNode.childNodes[childCount].removeAnimationForKey(key as! String)
              scene?.rootNode.childNodes[childCount].addAnimation(animation, forKey: key as! String)
            
          
          childCount++
        
           
     sceneView.scene = scene
     sceneView.autoenablesDefaultLighting = true            
  

如果有人能解释一下那就太好了!

啊,还有一个问题!

这是动画的开始帧:

这是我们想要结束的结束帧:

但在动画结束时,场景会跳回起始视图。

我通过修改动画“修复”了这个问题,使第 1 帧是最后一帧的副本。这行得通,并不引人注目,但似乎不是一个非常优雅的解决方案。

【问题讨论】:

【参考方案1】:

对于跳回第一帧的动画,isAppliedOnCompletion 值就是您要查找的值。

animation.repeatCount = 1
animation.isAppliedOnCompletion = true

这将确保动画在最后一帧暂停。

【讨论】:

【参考方案2】:

这对我有用。它基于您的答案,但会遍历节点的整个树并将其所有动画计数限制为一个。

我个人发现如果在手动遍历并将节点动画计数设置为1时错过了任何后代节点,我就会遇到问题。这样可以保证节点自己传入,所有子节点只会动画一次,完成后将模型保持在原位。

像这样使用animateEntireNodeTreeOnce(mostRootNode: nodeYouImportedFromCollada)

func animateEntireNodeTreeOnce(mostRootNode node: SCNNode)
    onlyAnimateThisNodeOnce(node)
    for childNode in node.childNodes 
        animateEntireNodeTreeOnce(mostRootNode: childNode)
    


func onlyAnimateThisNodeOnce(_ node: SCNNode) 
    if node.animationKeys.count > 0 
        for key in node.animationKeys 
            let animation = node.animation(forKey: key)!
            animation.repeatCount = 1
            animation.isRemovedOnCompletion = false
            node.removeAllAnimations()
            node.addAnimation(animation, forKey: key)
        
    

【讨论】:

【参考方案3】:

我知道这是一个老问题,但我遇到了同样的问题,并找到了跳回开始问题的解决方案。如果您将动画设置为在完成时不移除,则对象应停留在结束位置:

if let animation = child.animationForKey(child.animationKeys.first!) 
    animation.repeatCount = 1
    animation.removedOnCompletion = false
...

【讨论】:

以上是关于SceneKit - 停止连续循环 Collada 动画的主要内容,如果未能解决你的问题,请参考以下文章

从scenekit中的网络服务器加载collada文件

将带有物理的 collada 场景加载到 SceneKit

使用 SceneKit 从 DAE/COLLADA 中提取动画顶点

在 SceneKit 中支持 Collada/DAE 模型都有哪些要求?

SceneKit 进口的 COLLADA 盒子未“点亮”

将 collada (dae) 文件加载到 SCNNode (Swift - SceneKit)