飴屋

Flash3D/衝突・剛体・加速・固定

剛体

物理演算は現実世界の物理法則をプログラム内の物体に対しても適用することが目的ですから、まず、考えるべきことはプログラム内の物体に質量を付与することから始めます。しかし、プログラム内の物体は、表面をポリゴンでかたどったハリボテに過ぎませんし、各々のポリゴンの持つ情報は「各点の座標位置」と「点のつなぎ方」と「面の塗りつぶし方」だけです。

すなわち、質量の持つ「重さ」はこのポリゴンから直接導きだすことはできません。重さを厳密に計算する場合は、ポリゴンによって囲まれた容積を計算して、その物体を構成する原子やら分子やらの重さとの積を求める必要がありますが、計算するのが大変そうなので、およその数値を予め決めて設定するのが楽そうです。

質量を持つ物体Aと物体Bが交差しようとしたときに、ぶつかって跳ね返る現象(衝突)が発生します。液体や気体であれば分子間の結合が弱いので物体同士が混ざり合いますし、固体でも強くぶつかれば相手を跳ね返せずに自ら破損することがありますが、そういった元の物体の形状が変化してしまうような計算は複雑でリアルタイムに算出するのが困難です。なので、形が変わらない物体「剛体」という状態でjiglibflashは物体同士の衝突を計算します。

剛体は「jiglib.physics.RigidBody」というクラスで表されます。コンストラクタには、形状を表すポリゴンのデータが渡され、各物体を識別するIDが付与されます。しかし、剛体に渡されたポリゴンデータが複雑な場合、物体同士の衝突にポリゴン一枚ずつ計算を行うわけにはいきません。そこでRigidBodyでは、各ポリゴンを構成する点の位置(X,Y,Z)の最小値と最大値を元に、物体をすっぽり覆う直方体(AABBといいます)を擬似的に物体の形状と設定して衝突の計算を行います。直方体であれば6面分の衝突を判定するだけでいいので、計算量が激減します。ただし、物体の形状が直方体とは似ても似つかないような場合、AABBとの形状がかけ離れてしまい、衝突の結果が現実と乖離する恐れがあります。

そこでRigidBodyを継承していくつかの剛体のパターンが「jiglib.geometry」パッケージの中に用意されています。もっとも適した形状を選択することで、計算量を減らし、なおかつ現実的な衝突結果が得られるようになります。以下、剛体のパターンの紹介です。

JAABox
Axis-Alined Bounding Box(AABB)パターン。形状から勝手に決まってくれるので手軽で簡単。既定のパターンです。
JBox
幅、高さ、奥行きを指定できる直方体のパターン。
JCapsule
薬包のカプセルみたいな円柱の両端を球形に丸めた形状。円部分の径と長さを指定できます。
JPlane
平面のパターン。
JSphere
球のパターン。半径を指定できます。
JTerrain
地形のような凸凹した表面のパターン。私が使ってた時はなかったなぁ、これ。

同パッケージ内のJRayやJSegmentは物体のエッジを表現するのに内部で使われている様子でした。ここにはないパターンを自分で実装することもできると思いますが、RigidBodyを継承したクラスを作るだけではダメで後述の衝突を表すクラスを実装する必要がありそうです。

運動

剛体は時間とともに運動を行います。例えば、空中に一つ剛体を置けば、時間とともに重力に従って落下します。jiglibflashではどうやって剛体を運動させているのかみてみましょう。

まず、最初に物体が配置される空間を用意します。この空間の中では物理法則が適用されることになります。Papervision3Dと連携する場合は、jiglib.plugin.papervision3d.Papervision3DPhysicsクラスを使ってPapervision3Dのインターフェイスに特化した空間を規定できます。jiglibflashは複数の3Dエンジンに対応するために、同様のクラスをいくつか持っていますが、それらはjiglib.plugin.AbstractPhysicsクラスを継承して作られています。AbstractPhysicsはRigidBody(剛体)を追加・削除するメソッドや、空間内の時間進めるメソッドを持っています。addBodyメソッドで追加された剛体は物理法則に従って運動するわけですが、ご存知の通り、時が止まっているときに物体は静止していますので、随時、時間を更新してあげる必要があります。stepメソッドで時間を進行させる毎に、前回のstepメソッド実行時の現実世界の時間と現在の現実世界の時刻との差をとり、その差分だけプログラム内の仮想空間の時間を進めます。具体的には、剛体は直線運動のベクトルと回転運動のベクトルのプロパティを持っているので、差分時間あたりの運動量を計算して、剛体の位置と回転角を更新してあげれば、時間の経過を表すことができます。物理演算エンジンの計算結果を3Dエンジンに適用すれば、画面の描画まで完了します。

また、剛体は位置情報だけではなく、運動のベクトル情報自体も更新されます。例えば、重力によって下に向かうスピードは時間とともに加速しますので、時が進むほどベクトルの強さが増します。また、衝突によって、物体は静止したり運動の速度や向きが変化します。

実際にはjiglib.physics.PhysicsSystemの中に重力や衝突に関する記述がされています。integrateメソッドを覗いてみるとどのような順番で計算が行われているかわかりやすいですね。一度静止した物体にフラグをつけて、計算から外すという処理などが高速化に一役買っているみたいですね。

衝突

物体の衝突はいかにして検知すればよいでしょうか?jiglibflashの場合について軽く調べてみましょう。

PhysicsSystemのdetectAllCollisionsメソッドの中で各剛体の衝突状態を調べています。具体的な記述はjiglib.collision.CollisionSystemのdetectAllCollisionsメソッドの中にあります。剛体群の二つの剛体をチョイスしてそれぞれの現在位置とサイズを元にぶつかっているかどうかを確認し、ぶつかっていれば衝突情報(jiglib.collision.CollisionInfo)を作成して、剛体のプロパティとします。この衝突情報を元に剛体の位置を再計算してやれば、剛体の最終的な位置を3Dエンジンに伝えることができそうです。

衝突の判定は「球と球」「直方体と直方体」「球と直方体」とぶつかるものの形状ごとに計算方法が変わりますので、jiglib.collisionパッケージの中には全形状の組み合わせ毎に衝突を判断するクラスが用意されています。CollDetectSphereBoxなら球と直方体の衝突を検知します。球と球の衝突判定が一番簡単そうですよね。二者の中心間の距離が二者の半径の和より小さかったらぶつかってるってことになりますもんね。

衝突によって各剛体の運動ベクトルが変化することは前にも書きましたが、その詳細は物理を取り扱うサイトで調べてください。慣性とかベクトルの合成とか重心とか出てくるんでしょうねー。そうそうjiglibflashでは、剛体の表面材質についての設定が可能です。確か、弾性と摩擦に関するプロパティがjiglib.physicsMaterialPropertiesに定義されていました。これも衝突時の運動ベクトル変化に関連する事項ですね。

固定

jiglibflashには剛体の特定の点を特定の位置に固定する機能もあるようです。使ったことがないので、詳しくは知らないのですが、jiglib.physics.constraintパッケージあたりが関連すると思います。また、jiglib.physics.HingeJointなる骨関節のような固定をするクラスもありました。剛体の一部を固定すると、剛体の運動から自由度を取り除くことになります。一点で固定すれば、その点を中心とした回転運動しかできないでしょうし、二点を固定すれば、二点をつなぐ直線を軸とした回転運動しかできませんし、三点を固定すれば、すなわち剛体は運動できなくなるはずです。jiglibflashには地面のような完全に固定された面を設置することもできます。

完全に固定するのではなくて、ゴムみたいにある程度まで伸長して、次第に戻る運動を記述できれば、布みたいなものも記述できるのですが、jiglibflashにはまだなさそうです。クロスシュミレーションはちょっとやってみたいジャンルではあります。

Flash3D