このページの本文へ

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

SceneKitをARKitのプログラムに導入する

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

2018年08月03日 17時00分更新

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

  • この記事をはてなブックマークに追加
  • 本文印刷

 このところ数回は、SceneKitを使って仮想の3D世界に物体を配置し、それらをアニメーションで強制的に動かしたり、物理シミュレーションによって自発的に動かしてみたりしてきました。

 もう少し遡って思い出してみると、SceneKitに手をつける前には、ARKitを利用したプログラミングに取り組んでいました。その中で、ARKitで扱う仮想世界の部分は、ほぼ完全にSceneKitに頼っていることがわかりました。そこで、いったんARKitの探求を中断して、SceneKitに移行したのでした。

 もちろん前回までで、SceneKitの使い方を完璧にマスターしたというわけではありませんが、上で述べたように仮想世界を扱う仕組みは、ひととおりざっと理解できたのではないかと思います。そこで今回は、再びARKitに戻って、SceneKitでの経験を生かしたプログラミングを試してみることにします。

 以前にARKitを扱った際には、まず、ARKit自体が動作することを確認した後、現実世界の水平面(床や地面)を検出し、その上に静止した仮想物体を配置するところまでを試しました。その「静止した仮想物体」は、SceneKitによって実現したものでした。そして前回までで、SceneKitによって「動く仮想物体」を実現する方法もわかりました。そこで、今回は現実世界の映像の中に動く仮想物体を投影してみます。

 今回の最初には、シンプルな物理現象のシミュレーションによって、仮想物体が自然に動くようにします。次に、そこにジェスチャー認識機能を加え、現実世界の映像の中にある仮想物体に直接触れられるかのような感覚を味わえるようにします。そして最後に、アニメーション機能によって、仮想物体を現実世界の中で動き回れるようにしてみます。

「赤いガムテープ」を空中浮遊させる

 以前にも確認したように、3D仮想空間を投影するためのシーンビューは、ARKitでも使います。しかし、そのシーンビューはSceneKitのシーンビューと同じものではありませんでした。ARKitの中で使うために機能を強化したARKit専用のシーンビューとして、ARSCNViewクラスのオブジェクトを使います。

 とはいえ、その後の操作は、通常の、つまりSceneKitのシーンビューと大差ありません。例えば今回のプログラムでは、シーン全体に作用する重力をキャンセルするための設定を、viewDidLoadの中で実行しています。そのためのコードは、前回までに示したSceneKitのプログラムと何ら変わりません。

今回は、まずARKit用にチューンされたシーンビューであるARCSNViewのオブジェクトを作成し、それをビューコントローラーのビューに設定するところから始めましょう。後から出てくる水平面の検出機能を利用するため、ビューコントローラーはARSCNViewDelegateのプロトコルに準拠させます

 一方、ARKitで扱うシーンビューには、SceneKitでは必要がなかった操作も加える必要があります。それは、iPadのジャイロや重力センサー、カメラの映像などの変化によって現実世界をトラッキングする機能を起動したり、停止したりするものです。これは、以前と同様に、起動はviewWillAppear、停止はviewWillDisappearの中で実行しています。

ARKit特有の、現実世界のトラッキングのため、シーンビューのセッションを開始したり終了する処理は、それぞれviewWillAppearとviewWillDisappearで実行します。ここでは水平面の検出機能を有効にし、デフォルトの照明もオンにしています

 前者の中では、やはり以前同様、水平面を検出する機能を有効にし、デフォルトの照明もオンにしています。

 実際にシーンの中に物体(ノード)を配置するのは、水平面が検出されたときに呼ばれるrendererのdidAddメソッドの中です。今回は、検出された水平面に、平面のジオメトリ(SCNPlane)から作った透明のノードを配置することにしました。それは、そのノードに物理学場(SCNPhysicsField)を設定するためです。物理学場はノードに設定することで有効になるので、透明の平面でも立派にその役割を果たします。

 ここでは、まず検出された水平面よりも0.5だけ高い位置に透明の平面を配置しています。この面自体は動かないように、静止タイプの物理学体(PhysicsBody)も設定します。そして、これがいちばん重要ですが、周囲の物体を比較的強い力で引き付けるスプリングを、その水平面の中央に設定しました。

水平面を検出すると呼ばれるrendererのdidAddメソッドの前半では、検出したアンカー(ARPlaneAnchor)から面の大きさ位置の情報を取り出し、そこに透明な面のノードを配置しています。そのノードには、スタティックタイプの物理学体と、スプリングの物理学場を設定しました

 rendererメソッドの残りの部分では、SCNTubeのジオメトリから作った円筒形のノードを、透明の平面よりも、さらに0.2だけ高い位置に置いています。色は目立つように赤にしました。

rendererのdidAddメソッドの後半では、背の低い赤い円筒形のジオメトリから作ったノードを配置しています。位置は、検出した水平面からちょっと上にある平面のさらにちょっと上です。ダイナミックタイプの物理学場を設定しています

 これが物理学場の影響を受けるように、ダイナミックタイプの物理学体を設定しています。スプリングによる力の中心からちょっとずらすことで、最初からある程度の動きが付くことに期待しています。

 このプログラムを起動して、室内なら床やテーブル、屋外なら地面などの水平面を検出させると、その少し上に、赤い円筒系の物体が表示されるはずです。

プログラムを起動すると、検出した水平面の上に赤いガムテームのロールのように見える円筒の物体が浮かびます。最初からスプリングに引っ張られて何らかの動きが付いているはずですが、しばらくすると止まってしまうでしょう

 ちょうど赤いガムテームのロールのように見えるかもしれません。複数の水平面を検出すると、そのたびに赤い円筒形も追加されます。平面の中央に設置したスプリングの力は、同じシーン内のノードに影響するので、複数の水平面を検出させることで、赤いガムテープは、よりダイナミックに動きます。

この連載の記事

週間ランキングTOP5

ASCII倶楽部会員によく見られてる記事はコレだ!

ASCII倶楽部の新着記事

会員専用動画の紹介も!