Swift Playgroundsで学ぶiOSプログラミング

SceneKitのノードに動きを加えるプログラム

文●柴田文彦 編集●吉田ヒロ

2018年07月30日 17時00分

ジェスチャー認識機能と組み合わせてタッチしたノードだけを動かす

 ここまでの例では、プログラムが起動すると同時に無条件でアニメーションが始まっていました。次の例では、ユーザーの操作によってアニメーションを開始する方法を考えてみましょう。

 言うまでもなくiOSアプリでは、ユーザーの操作はほとんどが画面にタッチすることによるものです。そして、それをプログラムで検出するのが、ジェスチャーレコグナイザーでした。これまでは、2D画面へのジェスチャー操作を扱ってきましたが、3Dの世界ではタッチがどう処理されるのでしょうか。これは、タッチされたノードがどれなのかを検出することに限って言えば、非常に簡単です。SceneKitの描く3D世界は、結局2D画面に投影されているので、常にもっとも手前にあるノードには、ユーザーは直接タッチできるからです。

 まずは、タップ操作を検出するジェスチャーレコグナイザーを定義しましょう。これはviewDidLoadの中に書きます。1本指での1回のタップが検出されたらsceneTappedというメソッドを呼び出すレコグナイザーを作成し、それをsceneViewに張り付けています。

シーンの中に配置したノードのうち、ユーザーがタップしたノードにだけ、タップした時点からアニメーションを付けることができるように、ジェスチャーレコグナイザーを導入します。1本指で1回のタップに応答するものです

 タップが検出されると自動的に呼び出されるsceneTappedメソッドでは、まずsceneView内でタップされた点の二次元座標pointを求め、その点をシーンビューのhitTestメソッドに渡して、該当するノードを検出しています。実際には複数のノードが該当する可能性があるので、結果は配列として得られます。ここでは、その最初の要素を取り出してそこからノードを取り出しています。ちなみにhitTestメソッドは、SCNViewクラスのメソッドではなく、SCNSceneRendererプロトコルに定義されているものです。

ピラミッドのノードは前の例のものをそのまま使いますが、アニメーションは、タップジェスチャーの結果呼ばれるsceneTappedの中で開始します。シーンビューのhitTestメソッドによってタップされた位置にあるノードを検出し、そのノードに上の例と同じその場で回転のアニメーションを付けています

 検出されたノードには、とりあえず上の例と同じCore Animationによる回転のアニメーションを張り付けることにします。

 このプログラムを動かすと、上の例と同じ黄色のピラミッドが床の中央に配置されているのが見えます。ただし、最初はアニメーションが付いていません。そこで、おもむろにピラミッドをタップすると、その場で回転を始めます。

今度はタップするまでピラミッドは静止しています。タップした時点で、初めて回転し始めます。タップされたノードが何であるか判別していないため、床のノードをタップすると床も回転してしまいます

 このプログラムでは、タップされたノードが何であれ、それにy軸中心の回転アニメーションを適用します。そのため、床にタップすると、床も回転し始めます。

シーンアクションを使って、ノードに複雑な動きを加える

 今度、タップされたノードにやみくもにアニメーションを付けるのではなく、目的のノードがタップされたかどうかを確認する方法も見ていきましょう。また、それとは直接関係ありませんが、ここから、Core Animationとは別の方法でアニメーションを実現する方法に移ります。まずはSceneKit専用に用意されたシーンアクション(SCNAction)というクラスを使ってみましょう。

 その前に、新しいアニメーション方法で動かすノードも新調しましょう。アイスホッケーのパックのような、背の低い円筒形(SCNCylinder)のジオメトリから作ります。それを、こんどは中心からちょっと離れた位置に配置します。

別の種類のアニメーションを試すにあたって、アニメーションを付けるノードを改めましょう。今度は背の低い円筒です。それを床の中央ではなく、x座標、z座標とも10.0の位置に置いています

 ジェスチャーレコグナイザーから呼ばれるsceneTappedメソッドでは、まずこのパックがタップされたかどうかを調べます。シーンビューのhitTestメソッドを使って取り出したタップされたノードについて、それを作る時に使用したジオメトリが、どのクラスに属するものかを調べることができます。ここでは、そのクラスがSCNCylinderの場合だけ、SCNActionのアニメーションを付加する処理を実行しています。

今度は、sceneTappedメソッドの中でタップされたノードの種類を判別し、そらが円筒だった場合にだけSCNActionによるアニメーションを付けています。その際、4つの動きを1つのシーケンス(sequence)にまとめて、4つの動きが順番に起こるようにしてみました

 そのアニメーションは4つのアクションを組み合わせたものです。それらのアクションは、いずれも座標空間の中の平行移動を表すmoveで、最初はx軸のマイナス方向、2番めは、z軸のマイナス方向、3番めはx軸のプラス方向、4番目にz軸のプラス方向に移動して、元の座標に帰ってくるというものです。

 複数のアクションをsequenceとしてまとめることで、その中のアクションが順番に実行されます。つまり、1つのアクションが終わってから次のアクションが始まります。今回は例を示しませんが、複数のアクションをgroupとしてまとめると、それらを同時に実行することもできます。アクションは、ノードのrunActionメソッドで、繰り返し回数などを指定して起動します。

 このプログラムを実行すると、パックのような形のノードが床の上の中心から少し離れた位置に配置されます。そのパックをタップすると、アニメーションが始まり、正方形を描くように床の上を移動します。ちょうど5周回ったところで、アニメーションは止まります。

プログラムを起動してから背の低い円筒形のノードをタップすると、床の上を正方形を描くように動き回るアニメーションが始まります。このアニメーションは永遠に続くのではなく、ちょうど5周回ったところで止まります

 もちろん、その状態から再びタップすれば、またアニメーションが始まります。また、こんどは床をタップしても、床がアニメーションを始めてしまうことはありません。

シーントランザクションを使って複数のノードを同時に動かしてみる

 今回の最後に、もう1つのアニメーション方法を簡単に試しましょう。SceneKitに属するSCNTransactionクラスを使うものです。これは、そのクラスメソッドのbeginを実行してから、同じくcommitを実行するまでの間に記述したノードの変化をアニメーション表示するというものです。

 これまでに見てきたCore AnimationのaddAnimationや、SCNActionのrunActionのように、特定のノードにアニメーション機能を張り付けるわけではありません。そのため、beginとcommitの間には、いくらでも異なるノードの状態変化を書くことができます。結果として複数のノードを同時に動かすことができるという特徴を持ちます。

 ここでは、タップした円筒ノードの位置を床の上で平行移動するという、ごく単純なアニメーションを実行する例を示します。

もう1つのアニメーション、SCNTransactionも試してみましょう。このクラスのメソッドのbeginとcommitの間に書いたノードのパラメータの変化をanimationDurationで指定した秒数のアニメーションとして実行するものです。デフォルトで加速、減速の効果が付いています

 結果の画像は特に示しませんが、プログラムを動かしてみると、このアニメーションには動きに加速と減速がデフォルトで付いていることに気付くでしょう。ほかにもいろいろなノードを配置して、同時に動かしてみてください。

次回の予定

 今回は、シーンの中に配置したノードにアニメーション効果を付けるための、いろいろな方法を試してみました。はじめに述べたように、これらはいずれもノードの外部から強制的に動きを加えるものでした。次回は、シーンとノードに物理現象のシミュレーション機能を適用して、ノードが自発的に動くようにしてみる予定です。

mobileASCII.jp TOPページへ