在 Swift SceneKit 中使用 CAKeyframeAnimation 为 SCN 对象设置动画

Posted

技术标签:

【中文标题】在 Swift SceneKit 中使用 CAKeyframeAnimation 为 SCN 对象设置动画【英文标题】:Animating SCN Objects with CAKeyframeAnimation in Swift SceneKit 【发布时间】:2020-12-13 09:07:01 【问题描述】:

我正在编写一个以 3D 形式显示化学反应和分子的应用程序。我从文本文件中读取了每个原子的所有值和位置,并使用 SCNSpheres 创建了每个原子形状。我有我需要正确读取的所有其他值,但我不知道如何为场景中的每个节点对象添加关键帧动画。

我在 ViewController.swift 中设置了这样的分子

func makeAtom(atomName: String, coords: [Double], scene: SCNScene) 
        guard let radius = atomRadii[atomName]?.atomicRadius else  return 
        
        atoms.append(Atom(name: atomName, x: coords[0], y: coords[1], z: coords[2], radius: radius, positions: []))
        
        let atomGeometry = SCNSphere(radius: CGFloat(radius))
        let atomNode = SCNNode(geometry: atomGeometry)
        atomNode.position = SCNVector3(coords[0], coords[1], coords[2])
        scene.rootNode.addChildNode(atomNode)
        
        atomNodes.append(atomNode)
    

我知道 CAKeyframeAnimations 应该是这样设置的

let animation = CAKeyframeAnimation()
animation.keyPath = "position.y"
animation.values = [0, 300, 0]
animation.keyTimes = [0, 0.5, 1]
animation.duration = 2
animation.isAdditive = true

vw.layer.add(animation, forKey: "move")

我只是不知道我应该在哪里声明这些动画以及图层如何影响所有这些。我应该将动画添加到哪个图层?我怎样才能触发他们玩?我一直在整个互联网上搜索这方面的帮助,但我找不到任何只显示简单实现的东西。

如果需要,我可以提供更多代码,我是 *** 的新手,我想确保我做对了。

【问题讨论】:

【参考方案1】:

你可以用不同的方式来做,但我喜欢这种方法:58001288 (my answer here),因为你可以使用 scenekit 预先构建一些动画,然后将它们作为序列运行。

【讨论】:

这很好用,我现在唯一的问题是我想知道是否有办法访问当前正在运行的操作?例如,如果我要按下屏幕上的按钮,那一刻我可以访问当前正在执行的操作吗?【参考方案2】:

根据评论.. 需要更多空间。

序列是固定的。您可以开始、重复和停止它。但是,在阶段很难与之交互。

如果您确实需要这样做,那么一种方法是将您的序列分解为多个部分,并在当前序列的完成处理程序之后自己调用下一个。我保留了一个数组和一个计数器,以便我知道我在哪里。所以基本上它只是我管理的一系列操作 - 如果我在某个步骤上并且按下了按钮,那么我可以取消所有当前操作,设置所需的效果,然后重新启动它。

编辑:

完成处理程序在函数结束时调用自己并增加自己的数组计数,以便可以调用列表中的下一个。这显然有点危险,所以我会谨慎使用,但我就是这样做的。我在计时器上启动了我的,然后别忘了清理它。我有一个全局 GAME_ACTIVE 开关,并在代码中检查了它,然后再次调用自己。

Edit2:这实际上是一个 moveTo,但它仍然只是一组自定义的 SCNActions,它在完成时根据持续时间调用自己,以便它立即转到下一个而不会有延迟。

func moveTo()
    
        let vPanelName = moves[moveCount]
        let vLaneNode = grid.gridPanels[vPanelName]!.laneNodes[lane]
        let vAction = SCNAction.move(to: vLaneNode.presentation.position, duration: TimeInterval(data.getAttackSpeed(vGameType: gameType)))
        
        node.runAction(vAction, completionHandler:
            
                self.moveCount += 1
                if(self.moveCount >= self.moves.count - 1)
                
                    self.killMe(vRealKill: false)
                    return
                
                else
                
                    self.moveTo()
                
        )
    

【讨论】:

您介意分享一下您是如何做到的吗?我有我想要执行的 SCNActions 数组(不是作为序列),并且我得到了完成处理程序,但我不确定如何对其进行编码,以便它继续调用下一个。我猜它必须在一个循环中,但我只是不确定它会是什么样子。 很抱歉继续回到这个问题,但是完成处理程序中的动画代码正在到达,但它实际上不会执行动画。我错过了什么明显的东西吗? 你能分享你使用的代码吗?这不是微不足道的 - IMO,它相当棘手,但这是我知道的唯一方法,可以达到你可以按照你描述的方式实际与动作交互的水平。真的,您在那里所做的就是自己一次调用每个操作,并通过计时器调节更改。 ***.com/questions/63910293/… 我认为我做得对,因为它会工作一点点,但问题是原子动画有数百到数千步,我认为应用程序内存不足过了一会儿,整个事情都冻结了。这看起来是对的还是我可能做错了什么?

以上是关于在 Swift SceneKit 中使用 CAKeyframeAnimation 为 SCN 对象设置动画的主要内容,如果未能解决你的问题,请参考以下文章

swift 在swift中使用SceneKit制作一个圆柱体

如何将 SCNFloor 的属性与 Swift 和 Scenekit 一起使用?

在 Swift SceneKit 中使用 CAKeyframeAnimation 为 SCN 对象设置动画

在 SCNSphere 上获取点击位置 - Swift (SceneKit) / iOS

Swift 3 - SceneKit 获取 3d 模型比例值

游戏结束后如何在 Swift SceneKit 游戏中启用广告?