SceneKit - 如何停止连续循环的Collada动画

6
我将使用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
        }
    }

我也尝试了另一种方法:
让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文件图标有一个播放按钮,点击它也会循环播放图标内部的动画。

更新:

现在我注意到在上面的代码中,我正在设置常量“animation”属性,但这并没有被复制回实际的场景节点。此外,.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            
  }

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

啊,但还有另一个问题!

这是动画的起始帧:

Start frame

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

End frame

但在动画的结尾处,场景会跳回到起始视图。

我通过修改动画使第1帧成为最终帧的副本来“修复”此问题。 这可以工作,并且不会引起注意,但似乎并不是一个非常优雅的解决方案。

4个回答

3

如果你希望动画跳回第一帧,那么isAppliedOnCompletion就是你要找的值。

animation.repeatCount = 1
animation.isAppliedOnCompletion = true

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

2

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

就我个人而言,如果我在手动遍历并将节点动画计数设置为一时,错过了任何后代节点,我会遇到问题。这确保了传入的节点本身和所有子节点只会播放一次动画,然后在完成后保持模型不动。

使用方法如下:animateEntireNodeTreeOnce(mostRootNode: 从Collada导入的节点)

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)
        }
    }
}

2

我知道这是一个老问题,但我遇到了同样的问题,并找到了解决跳回开头问题的方法。如果您将动画设置为在完成后不被删除,则对象应该停留在最终位置:

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

0

我同意@Phil Dudas的观点,并且我想补充一下这个解决方法,以防止动画默认启动,通过将速度降为零来实现。当您将速度设置为零时,它将保持在第一个状态。

                    nodeAnimation.speed = 0
                
                    animation.repeatCount = 1 
                    nodeAnimation.animation.isRemovedOnCompletion = false

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接