SwiftSceneKitでCAKeyframeAnimationを使用してSCNオブジェクトをアニメーション化する
化学反応や分子を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")
これらのアニメーションをどこで宣言する必要があるのか、レイヤーがこれらすべてにどのように影響するのかがわかりません。アニメーションをどのレイヤーに追加する必要がありますか?そして、どうすればそれらを再生するようにトリガーできますか?私はこれについてインターネット全体で助けを求めてきましたが、単純な実装を示しているだけのものは見つかりません。
必要に応じてさらにコードを提供できます。StackOverflowは初めてで、これが正しく行われていることを確認したいと思います。
回答
さまざまな方法で実行できますが、私はこの方法が好きです。シーンキットを使用していくつかのアニメーションを事前に作成し、それらをシーケンスとして実行できるため、58001288(ここでの私の答え)。
コメントによると..より多くのスペースが必要でした。
シーケンスは固定されたものです。開始、繰り返し、停止することができます。ただし、フェーズ中にそれと対話することは困難です。
本当にそれを行う必要がある場合、1つの方法は、シーケンスをその部分に分割し、現在のシーケンスの完了ハンドラーの後で自分で次のシーケンスを呼び出すことです。自分がどこにいるかがわかるように、配列とカウンターを保持しています。つまり、基本的には、私が管理するアクションのキューにすぎません。特定のステップでボタンが押された場合、現在のすべてのアクションをキャンセルし、目的の効果を設定して、再開できます。
編集:
完了ハンドラーは、関数の最後でそれ自体を呼び出し、リスト内の次の配列を呼び出すことができるように、独自の配列カウントを進めます。これは明らかに少し危険なので、控えめに使用しますが、それが私がやった方法です。私はタイマーで私のことを始めました、そしてそれをきれいにすることを忘れないでください。私はグローバルGAME_ACTIVEスイッチを持っていて、コード内でそれをチェックしてから、もう一度自分自身を呼び出しました。
Edit2:これは実際にはmoveToですが、それでも、継続時間に基づいて完了時に自分自身を呼び出すSCNActionのカスタムセットであるため、遅延なしですぐに次のアクションに進みます。
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()
}
})
}