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")
同じプロパティを手動で設定してアニメーション化しようとすると、問題が発生する可能性があります。 byValue
アニメーションを使用すると、問題がさらに悪化します。これは現在の変換に連結するため、現在の変換がアニメーションの開始時に期待するものであるかどうかを追跡するのが難しくなります。
代わりに、ピラミッドの固定された方向(その頂点は-z方向)とアニメーション(それが指す軸の周りを回転する)を分離します。これを行うには2つの良い方法があります。
pyramidNode
を1回の回転(x軸を中心としたπ/ 2)を取得する別のノードの子にし、スピンアニメーションを直接pyramidNode
に適用します。 (この場合、ピラミッドの頂点はローカル空間の+ y方向を指しているため、z軸ではなくその軸を中心に回転する必要があります。)
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ビットアーキテクチャでは特に機能しなくなります。HUGE
の代わりに.infinity
を使用します。これは、spin.repeatCount
のタイプに関係なく、すべての浮動小数点タイプに対して定義されている定数値を使用します。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つずつ削除していき、適切な変換を見つけることができます。
SceneKitには、CAAnimationsよりもはるかにシンプルで短いアニメーションヘルパーが含まれています。これはObjCですが、要点は次のとおりです。
[pyramidNode runAction:
[SCNAction repeatActionForever:
[SCNAction rotateByX:0 y:0 z:2*M_PI duration:3]]];
私はbyValueをtoValueに変更しましたが、これでうまくいきました。だから行を変更...
spin.byValue = NSValue(SCNVector4: SCNVector4(...
に変更...
spin.toValue = NSValue(SCNVector4: SCNVector4(x: 0, y:0, z:1, w: 2*float(M_PI))