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

SceneKitの物理現象シミュレーションとアニメーションをARKitに持ち込む

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

2018年08月03日 17時00分

空中の物体にタッチしてエネルギーを与える

 このままでは、外部からノードに力を加えることができないので、ただ観察するしかありません。やがて力が釣り合うと、ノードの動きも止まってしまいます。

 そこで今度は、ノードにタッチしてフォースを加えられるようにしてみましょう。まずは、viewDidLoadメソッドの中で、タップジェスチャーに対するレコグナイザーを定義します。

画面の中に浮遊している赤いガムテープをタップすることで指で突くようなフォースを与えられるように、1本指による1回のタップを検出するジェスチャーレコグナイザーを設定します

 ジェスチャーを検出すると呼ばれるsceneTappedメソッドの中では、例によって画面上のポイントからタップされたノードを割り出し、そこに3要素のベクトル(SCNVector3)でランダムに作ったフォースを加えます。x、y、z軸ともに、ベクトルの要素が-0.5〜0.5の範囲となるようにしています。力を加える位置もランダムにずらして、回転力が加わるようにしています。

ジェスチャーに反応するsceneTappedメソッドの中では、ユーザーがタップした画面上の座標点から実際にタップされたノードを割り出し、そのノードにランダムな方向、ランダムな強さ、ランダムな位置のズレをともなったフォースを与えています

 このプログラムを動かして水平面を検出すると、やはり赤いガムテームが出現します。ここまでは上のプログラムと同じですが、画面上で赤いガムテープにタップすると、こんどはランダムなフォースが加えられガムテープは回転しながら飛び回るようになります。

プログラムが現実世界の水平面を検出すると、その上に赤いガムテープのロールが浮かぶところまでは上のプログラムと同じです。こんどは画面をタップすることで、浮かんでいる赤いガムテープに指で突いたような動きを加えることができるようになりました

 複数のガムテープを出現させると、互いに衝突することもあってかなりダイナミックな動きになるでしょう。iPadを動かしてガムテープの動きを追いながらタップすれば、拡張現実の臨場感を味わうことができるはずです。

アニメーション機能によって、床の上に配置した自動車のモデルを走らせる

 今度は以前にも使った単純な自動車の3Dモデルを含むシーンファイルを読み込んで、そこから抽出したノード、つまりクルマを走らせてみることにしましょう。物理シミュレーションを使って上のようにフォースを加えて動かしてもいいのですが、1つ問題があります。重力があると、水平面のないところではクルマが下に落ちてしまうことです。もちろん重力をキャンセルしてもよいのですが、それだと無重力状態で車を動かすことになり、物理シミュレーションの意味が半減してしまいます。

 そこで、ここでは物理シミュレーションはあきらめ、単純なアニメーション機能によって、クルマを周回させてみることにしました。

 viewDidLoadメソッドの中では、とりあえずプレイグラウンドのリソースとして追加したシーンファイル「Car.scn」を読み込んでおきます。クルマへのタッチによって動かし始めるので、タップジェスチャーのレコグナイザーは上の例と同じです。

今度のプログラムでは、以前のARKitの回でも使ったクルマのモデルを含むシーンファイル「Car.scn」を再び読み込んで利用します。シーンビューとともに、シーンのプロパティ(carScene)を用意して、viewDidLoadの中でファイルから中身をロードします

 rendererメソッドの中では、検出した水平面に重なる平面ノードはやめて、ファイルから読み込んだシーンから抽出したクルマのノードだけを配置することにしました。物理シミュレーションはあきらめたので、床や地面が物理学体を設定したノードとして存在している必要はないからです。

プログラムが水平面を検出したら、その際に渡されるアンカーの位置にクルマのノードだけを配置します。水平面の場合と同様に、クルマのノードもx軸を中心に-90度ほど回転させると正しい向きになります。さらに、y軸を中心に-90度ほど回転させて進行方向に向けておきます

 この例では、読み込んだシーンに含まれる「Car」という名前のノードを抽出しています。もし後から、このノードをほかのノードと区別する必要が出てきたとき、ジオメトリから生成したわけではないので、ジオメトリのクラスで識別することはできません。そのとき、この「Car」という名前が役立ちます。今回は、配置するノードは1つだけでなので、識別は必要ないのですが、もし必要になった場合に備えて、あとでその方法を示します。

 なお、続けて水平面が検出された場合、シーンからノードを検出する処理が失敗することがあるようです。それに備えて、ノードを抽出する部分のコードの先頭にguardステートメントを入れています。これでもしうまく検出できない場合は、何もなかったことにしてrendererメソッドの続きの実行をキャンセルします。

 タップジェスチャーに対する処理は少し長くなっていますが、やっていることは単純です。一定距離だけ直進して90度回転するという動作を4回繰り返すアニメーションを定義して実行しているだけです。つまりクリマは正方形を描くように走ります。これらの合計8つの動作は1つのシーケンスにまとめているので、すべて1つのアクション(SCNAction)が終わってから次のアクションと順番に動きます。

 このメソッドの前半部分では、タップされたノードの名前を調べてそれが「Car」の場合にだけ、アクションによるアニメーションを実行するようにしています。4つの直進の動作は、それぞれ最初に加速して始まり最後に減速して終わるようにするため、いったんアクションを定義してから、そのtimingModeプロパティに.easeInEaseOutを設定しています。

ユーザーがクルマのノードをタップすると、アクション(SCNAction)によるアニメーションで車を走らせます。アクションは、直進して右に90度回転という動作を4回繰り返すもので、直進部分には加速と減速のタイミングモードも設定しています

 後半部分は、全部で8つのアクションを1つのシーケンスにまとめてから、そのシーケンスを5回繰り返して実行するようにrunActionメソッドによってアニメーションを起動しています。

直進、右回転、直進、右回転、直進、右回転、直進、右回転、という全部で8つのアクションをシーケンスとして1つのアクションにまとめてから5回の繰り返しを指定して実行しています。シーケンスなので個々のアクションが順番に1つずつ実行されます

 このプログラムを動かして床や地面を検出させると、その上に以前にも見た赤いクルマが配置されるはずです。そのクルマをタップすると、正方形を描いて周回するアニメーションが始まります。

認識した水平面の上にクルマのモデルが表示されたら、それをタップすると走り出します。同じ距離だけ直進して90度右折という動作を繰り返すので、正方形を描いて走ることになります

 このプログラムでは水平面しか検出していないので、場合によっては障害物に当たっても、すり抜けてそのまま走行することになってしまうかもしれません。

次回の予定

 今回は、前回までに培ったSceneKitの扱いを以前に中断していたARKitのプログラムに導入しました。それによって、カメラで撮影する現実世界と動きのある仮想世界とのより緊密な融合を図りました。現実と仮想の区別が付きにくいような雰囲気が少しでも感じられれば、その試みも成功したと言えるでしょう。

 この連載ではこれまで、可能な限りシンプルにプログラミングすることを大前提に、iOSのさまざまな機能をプレイグラウンドで実現することにチャレンジしてきました。カレンダーや連絡先、位置情報など、iOSアプリとして、特別なパーミッションが必要なプライバシーに関わる機能を除けば、ほとんどのことがiPadのSwift Playgroundsだけでかなり簡単にできてしまいます。この連載で示したプログラムによって、少なくともそれだけは実証できたのではないかと自負しています。

 突然で申し訳ありませんが、この連載も今回で最後とさせていただくことになりました。これまでのご愛読に感謝します。どんな環境であれ、これからもプログラミングを楽しんでください。

mobileASCII.jp TOPページへ

mobile ASCII

Access Rankingアクセスランキング