Experts.jp日本に、もっとエキスパートを。Fowlsというゲームについての技術解説後編です。物理エンジン前編ではスリングショットコントローラーについて説明しましたが、ゲーム本体の技術要素についてはほぼ触れていませんでした。Fowlsでは弾が飛んでいく動きや、弾がぶつかって崩れるブロックの動きなどを実現するために、物理エンジンを使用しています。物理エンジンは文字通り、物理的な運動をコンピューター上で模擬してくれるライブラリで、特に今回のアプリのような物体同士の衝突を処理する必要がある場合に有用です。Fowls技術解説後編では、この物理エンジンについて簡単に説明します。cannon.jscannon.jsについて3次元を扱えるものに限っても、JavaScriptで実装された物理エンジンはいくつかあります。Fowlsではその中からcannon.jsという物理エンジンを採用しました。これは他の物理エンジンの多くが他言語製物理エンジンの移植で、インターフェースに違和感があったのに対して、cannon.jsはJavaScriptで新たに作成された物理エンジンなので、インターフェースが素直に見えたことと内部構造がシンプルだったためです。Fowlsから抜き出したものになっています。単純化したコードの全文は以下にありますので、適宜参照してください。全体的な流れ物理エンジンは、物体の位置や向きを計算してくれるだけで、物体の表示には関知しません。表示に関しては、three.jsをいつも通りに使うことになります。両者を組み合わせる場合の基本的な流れは、次のようになります。物理エンジンとthree.jsで、別個に物体を扱っているのが冗長に感じられるかもしれませんが、これには理由があります。両者を区別していることで、1つの物体に対して2種類のモデルを使用できるのです。three.js内で複雑な人型として扱われているオブジェクトを、物理エンジン内では単なる直方体として扱い、当たり判定を簡略化することができます。上記のリストで重要なのは、5と6-2です。基本的にはオブジェクトの位置と向きは物理エンジンが管理し、オブジェクトの(表示される)形状はthree.jsが管理しますが、それぞれの情報の関連付けは開発者に委ねられます。5と6-2は、この情報の関連付けを処理しています。本記事での解説は、上記の物理エンジンに関わる部分を抜き出す形で行います。...snip...},}cannon.jsでは、物理エンジンの実体はCANNON.Worldオブジェクトです。物理エンジン全体に関わる設定や処理は、このオブジェクトを通じて行います。設定しているプロパティはそれぞれ、gravityは名前の通り重力、broadphaseは「衝突している可能性のあるオブジェクトの組み合わせを見つけるアルゴリズム」です(注1)。設定できる値はほかにもありますが、とりあえずこの2つをこの通りに設定しておけば動作します。Phaseで厳密に検査します。Phaseのアルゴリズムは、物理エンジンのパフォーマンスに大きく影響を与えるため、cannon.jsではプロパティとして与える形でその変更を容易にしています。...snip...},}cannon.jsでは、物体はCANNON.RigidBodyオブジェクトとして表され、物体の位置や向きの設定などはこのオブジェクトを通じて行います。物体の形状はCANNON.RigidBodyオブジェクトからは分離されていて、その指定にはCANNON.Shapeオブジェクトのサブクラスを使用します。上記の例では、CANNON.Plane(平面)とCANNON.Sphere(球体)を利用していますが、それ以外にも以下のような形状が使用可能です。上記の組み合わせなお平面はデフォルトではz軸に垂直ですが、cannon.jsでは重力はy軸に垂直なので、地面として使用するためにquaternionを設定して、x軸に対して90度回転させています。物理エンジンの物体とthree.jsの物体を関連付け物理エンジンの物体とthree.jsの物体を関連付けについては、特に決まったやり方はありません。管理する物体の数が少なければ、単純に1つの物体に対してcannon.js用とthree.js用、2種類のオブジェクトを用意すればいいでしょう。今回の単純化したコードでは、threeFooとcannonFooという2変数で管理しています。Fowlsでは、three.jsのオブジェクトとcannon.jsのオブジェクトを1つにまとめて使うためのc3.jsという簡単なライブラリを作成しましたが、まだ公開するような完成度ではないので、ここでは説明を省略します。次の位置を計算JavaScriptthis.cannonWorld.step(1.0/24.0);1this.cannonWorld.step(1.0/24.0);物体の位置を更新するには、CANNON.Worldオブジェクトのstepメソッドに経過時間を渡します。これだけで先にaddした物体間の衝突が処理され、位置や向きがそれぞれの運動状態に応じて更新されます。this.threeBird.quaternion.copy(this.cannonBird.quaternion);12this.threeBird.position.copy(this.cannonBird.position);this.threeBird.quaternion.copy(this.cannonBird.quaternion);先ほどCANNON.Worldオブジェクトのstepメソッドを呼び出しましたが、このメソッドはCANNON.RigidBodyの位置と向きを更新するだけです。表示位置を更新するには、描画の前にthree.js側のオブジェクトとcannon.jsのオブジェクトの位置を同期する必要があります。cannon.jsはthree.jsと組み合わせて使うことが初めから想定されているため、これは非常に簡単です。three.jsのオブジェクトのpositionプロパティ(位置)とquaternionプロパティ(向き)に、cannon.jsのオブジェクトのそれらをcopyしてください。これだけで位置と向きの同期が実現されます。ここまでの実装を行うと、次のように衝突がそれっぽく扱われるようになります。Fowlsはthree.jsに用意されている基本図形だけでできています。それでも物理エンジンを使用すれば、その動きで見た目をある程度はカバーできていると言っていいのではないでしょうか。今回の記事から分かる通り、物理エンジンの利用は存外に簡単です。初期位置を登録してstepメソッドを呼び出し結果を表示に反映させる、これだけです。three.jsを使用して単なるビューアーに留まらないインタラクティブなアプリを作るのであれば、物理エンジンを採用しない理由はありません。本記事を参考に、ぜひいろいろと試してみてください。