SceneKitのノードをアニメーションさせる方法2:Core Animation / magicien 

SceneKitのノードをアニメーションさせる方法として、Core Animationを使用することもできる。

公式の説明から例を引用すると、
let animation = CABasicAnimation(keyPath: "geometry.extrusionDepth")
animation.fromValue = 0.0
animation.toValue = 100.0
animation.duration = 1.0
animation.autoreverses = true
animation.repeatCount = .infinity
textNode.addAnimation(animation, forKey: "extrude")
こんな感じ。CABasicAnimationやCAPropertyAnimationの場合は、どの値を変更するかをkeyPathで指定する。
上記の場合、textNode.geometry.extrusionDepthの値が0.0から100.0へ、1秒間かけて変更され、その後、1秒間かけて100.0から0.0に戻される。以降、繰り返し。
addAnimationの時に指定するキーは、アニメーションを止めたり消したりする時に使うためのものなので、追加した後、特に制御する予定がなければ nil でも良い。
repeatCountが .infinity でない場合は、アニメーション終了時に自動的にアニメーションが削除される。
アニメーションが削除されると、アニメーション適用前の値に戻ってしまうので、要注意。(SCNTransactionの場合は、アニメーション後も値は変更されたまま)
アニメーション終了時の値のままにしたい場合は、アニメーションが消えないように、
animation.isRemovedOnCompletion = false
を設定してやると良い。ただ、手動でアニメーションを削除してしまえば、やっぱり値は元に戻ってしまう。
アニメーションが消えても値を維持したいなら、アニメーション終了時に値を代入してやれば良い。
func hoge() {
    ....
    animation.delegate = self
    ....
}

func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
    if(flag){
        // アニメーション終了時にここが実行されるので、改めて値を入れてあげたり。
    }
}
新APIだと、Core Animationの他に、SCNAnimationというのが使えるようになったらしい。
また、addAnimationの代わりに、addAnimationPlayerを使うのが推奨されている。(まだ試してない)
使うとしたらこんな感じ。
let animation = SCNAnimation(caAnimation: CABasicAnimation(...)) // 結局中身はCAAnimation
animation.animationDidStop = { (animation: SCNAnimation, obj: SCNAnimatable, finished: Bool) in
    // 終了時の処理。delegate使うより個別に設定できるし使いやすそう。
}
let player = SCNAnimationPlayer(animation: animation)
node.addAnimationPlayer(player, forKey: nil)
player.play()
addAnimationだと、追加した瞬間にアニメーション開始しちゃったけど、SCNAnimationPlayerを使えば、好きなタイミングで開始できてちょっと便利。

MMDみたいなキーフレームアニメーションの場合は、CAKeyframeAnimationを使って実現できる。また、複数のアニメーションを同期させる場合は、CAAnimationGroupを使う。

ちなみに、アニメーション中の値を取得する場合は、node.presentation を参照する必要がある。
let animation = CABasicAnimation(keyPath: "position.x")
animation.toValue = 1.0
animation.duration = 2.0

node.position.x = 0.0
node.addAnimation(animation, forKey: nil)
例えば、上記コードを実行した1秒後に node.position.x の値を取得しても 0.0 のままになっている。
node.presentation.position.x の値を取得すると 0.5 になっている。
presentationの値は直接変更できない(できるかもしれないけど、変更するとSceneKitが困ってしまう)ので、値を変えたい場合は、通常通り node のパラメータを変更すること。

2017/06/18(Sun) 14:27:30