web-dev-qa-db-ja.com

SceneKit – SCNNodeの回転とアニメーション

Z軸に沿ってポイントし、Zを中心に回転するピラミッドを表示しようとしています。私のカメラはz軸上にあるので、上からピラミッドが見えると期待しています。このようにピラミッドを回転させて見ましたが、アニメーションを追加すると、複数の軸で回転しているように見えます。

これが私のコードです:

// The following create the pyramid and place it how I want
let pyramid = SCNPyramid(width: 1.0, height: 1.0, length: 1.0)
let pyramidNode = SCNNode(geometry: pyramid)
pyramidNode.position = SCNVector3(x: 0, y: 0, z: 0)
pyramidNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: Float(M_PI / 2))
scene.rootNode.addChildNode(pyramidNode)

// But the animation seems to rotate aroun 2 axis and not just z
var spin = CABasicAnimation(keyPath: "rotation")
spin.byValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: 2*Float(M_PI)))
spin.duration = 3
spin.repeatCount = HUGE
pyramidNode.addAnimation(spin, forKey: "spin around")
16
Newalp

同じプロパティを手動で設定してアニメーション化しようとすると、問題が発生する可能性があります。 byValueアニメーションを使用すると、問題がさらに悪化します。これは現在の変換に連結するため、現在の変換がアニメーションの開始時に期待するものであるかどうかを追跡するのが難しくなります。

代わりに、ピラミッドの固定された方向(その頂点は-z方向)とアニメーション(それが指す軸の周りを回転する)を分離します。これを行うには2つの良い方法があります。

  1. pyramidNodeを1回の回転(x軸を中心としたπ/ 2)を取得する別のノードの子にし、スピンアニメーションを直接pyramidNodeに適用します。 (この場合、ピラミッドの頂点はローカル空間の+ y方向を指しているため、z軸ではなくその軸を中心に回転する必要があります。)

  2. pivotプロパティを使用して、pyramidNodeのコンテンツのローカルスペースを変換し、pyramidNodeを、それを含むスペースに対して相対的にアニメーション化します。

2番目のアプローチを示すコードを次に示します。

let pyramid = SCNPyramid(width: 1.0, height: 1.0, length: 1.0)
let pyramidNode = SCNNode(geometry: pyramid)
pyramidNode.position = SCNVector3(x: 0, y: 0, z: 0)
// Point the pyramid in the -z direction
pyramidNode.pivot = SCNMatrix4MakeRotation(CGFloat(M_PI_2), 1, 0, 0)
scene.rootNode.addChildNode(pyramidNode)

let spin = CABasicAnimation(keyPath: "rotation")
// Use from-to to explicitly make a full rotation around z
spin.fromValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: 0))
spin.toValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: CGFloat(2 * M_PI)))
spin.duration = 3
spin.repeatCount = .infinity
pyramidNode.addAnimation(spin, forKey: "spin around")

コード品質を改善するためのいくつかの無関係な変更:

  • CGFloatコンポーネントを初期化するために明示的な変換が必要な場合は、SCNVectorを使用します。 FloatまたはDoubleを使用すると、32ビットまたは64ビットアーキテクチャでは特に機能しなくなります。
  • 従来のBSD数学定数HUGEの代わりに.infinityを使用します。これは、spin.repeatCountのタイプに関係なく、すべての浮動小数点タイプに対して定義されている定数値を使用します。
  • Π/ 2の正確さを理解するには、M_PI_2を使用します。
  • letには別の値を割り当てないため、アニメーションにはvarではなくspinを使用します。

CGFloatエラービジネスの詳細:Swiftでは、数値リテラルには、その式が必要とするまで型がありません。そのため、spin.duration = 3のようなことができます-durationは浮動小数点値ですが、Swiftを使用すると、「整数リテラル」を渡すことができます。ただし、let d = 3; spin.duration = dエラーが発生します。なぜですか?変数/定数には明示的な型があり、Swiftは暗黙的な型変換を行いません。3は型なしですが、割り当てられるとdの場合、他に何も指定していないため、タイプ推論のデフォルトではIntが選択されます。

型変換エラーが発生している場合は、リテラル、定数、関数から返された値を組み合わせたコードが含まれている可能性があります。おそらく、式のすべてをCGFloat(またはその式に渡す型が何であれ)に変換することで、エラーを解消できます。もちろん、その場合はコードが判読できなくなり、見苦しくなります。そのため、コードが機能するようになったら、変換を1つずつ削除していき、適切な変換を見つけることができます。

16
rickster

SceneKitには、CAAnimationsよりもはるかにシンプルで短いアニメーションヘルパーが含まれています。これはObjCですが、要点は次のとおりです。

[pyramidNode runAction:
    [SCNAction repeatActionForever:
        [SCNAction rotateByX:0 y:0 z:2*M_PI duration:3]]];
14
Graham Perks

私はbyValueをtoValueに変更しましたが、これでうまくいきました。だから行を変更...

spin.byValue = NSValue(SCNVector4: SCNVector4(...

に変更...

spin.toValue = NSValue(SCNVector4: SCNVector4(x: 0, y:0, z:1, w: 2*float(M_PI))
0
FlippinFun