WOWEngineという、ActionScript3.0で使える3D物理エンジンを、
2D平面の中での物理演算フレームワークはAPEとか、Box2DFlashAS3とかFisixとか他にもいろいろあったけど、最近初めて3D空間上で物理演算するフレームワークWowEngineが出てきました。
feb19.jp blog – 3D物理演算エンジンWOWEngineとPapervision3D 2.0 GreatWhite
を読んで知ったので、早速試してみた。前回の記事の、ミクとリンが動くサンプルを改良して、ミクとリンがぶつかって跳ね回るようにしてみた。
モデルは相変わらずズサさん。
とりあえずモノはコチラ。
こんな機能がある。
- 床部分にマウスを置いて動かすと、ミクとリンがマウスの方を向く
- 床をクリックすると、ミクとリンがクリックした点に向かって突進
- ミクとリンがぶつかると、反発しあって互いに吹っ飛ぶ。
ちなみに、最初の天から降ってくるギミックが完了するまでは動かせなくしてある。あの部分はWOWEngineではなく、Tweenerを使ってる。WOWEngine使ったら、跳ねまくってえらいことになったので。
と言うことで、今回のエントリではWOWEngineの使い方を紹介。
WOWEngineを使う手順は以下の通り。
- WOWEngineを入手する
- WOWEngineを初期化する
- 物体を追加する
- 物体を動かす
- WOWEngineで計算する
- 表示に反映する。
なお、WOWEngineは表示関係の一切を行わない、物理計算のみのライブラリであることに注意。要するに、WOWEngine内で物体を定義し、その物理的な動きを計算した上で、その結果(座標値など)を表示系の方に渡す必要がある。今回の場合、Papervision3Dが表示を担当する。
WOWEngineが表示にタッチしないお陰で、今回のミクやリンのように見た目が複雑なモデルでも、単なる球体として計算を行うことが出来ると言う利点がある。
WOWEngineを入手する。
まずは、WOWEngineを公式サイトからダウンロードして、クラスパスを通す。この時注意する必要があるのは、WOWEngineが使用している依存ライブラリの存在。
WOW-Engine use and depend of the Data Structures classes written by polygonal labs.
shirotokoro » AS3 3D Physics Engine : WOW-Engine
上記のように、WOWEngineは「AS3 Data Structures For Game Developers」を利用している。なので、これもダウンロードしてクラスパスを通す。
なお、WOWEngineは変数や関数に型を設定していない部分が幾つかあるので、コンパイル時に警告が出てくる。気になる場合は修正しよう。
WOWEngineを初期化する。
ここからは実際にWOWEngineを使う話。まずは、3D物理計算を行ってくれるエンジンである、「WOWEngine」のインスタンスを作成・初期化する必要がある。
以下のような感じで、クラスのメンバとして定義する。
- private var wow:WOWEngine;
- private var wowFloor:WBoundArea;
- private var wowMiku:WSphere;
- private var wowRin:WSphere;
必須となるのが、WOWEngine
クラスのインスタンス「wow」と、物理計算を行う領域を表すWBoundsArea
のインスタンス「wowFloor」だ(名前は任意)。
残りの2つのWSphere
は、メンバ名からも想像出来る通り、ミクとリンを表す物体である。
このようにして宣言したWOWEngine関連のメンバを、次のように初期化する。
- wow = new WOWEngine();
- wow.collisionResponseMode = wow.SELECTIVE;
- wow.addMasslessForce(new WVector(0, 0, 0));//常にかかり続ける力
- wow.damping = 1;
- wowFloor = new WBoundArea(FLOOR_WIDTH , FLOOR_HEIGHT , FLOOR_DEPTH);
- wowFloor.setPosition(0, FLOOR_HEIGHT / 2, 0);//位置
- wowFloor.elasticity = 1;//弾性
- wowFloor.friction = 0.050;//摩擦
- wow.setBoundArea(wowFloor);
まず、先頭4行でWOWEngine
のインスタンスを作成し、初期設定を行っている。それぞれの設定は以下の意味を持つ。
wow.collisionResponseMode
- これは、衝突時の計算の詳細度。この値が小さいほど、物体同士が衝突した時に細かく動き回る。定数として、STANDARD(100)、SELECTIVE(200)、SIMPLE(300)がある。
wow.addMasslessForce
- 空間内に常にかかり続ける力を設定する関数。
WVector
は、WOWEngine内での「物体にかかる力」を意味している(と言うか、物理で言うベクトル)。
コンストラクタの書く引数は以下の意味を持つ。- new WVector(X方向のベクトル,Y方向のベクトル,Z方向のベクトル);
wow.addMasslessForce
で設定されたベクトルは、wow
内に登録された全ての物体に常に働く。なので、WVector(0,-3,0)
のようにして、重力代わりに使う人が多いようだ。 wow.damping
- 残念ながら詳細は良くわからないが、設定されたWVectorに対して係数のように働く値らしい。これを0にするとどんなに力を与えても物体は動かなかった。1を超える力を与えると暴れまわって物体はどこかに消えた。
次に、WBoundsArea
の設定を行う。それぞれ以下の意味を持つ。
new WBoundArea(FLOOR_WIDTH , FLOOR_HEIGHT , FLOOR_DEPTH)
WBoundsArea
の初期化。引数はそれぞれ、(幅、高さ、奥行き)で、立方体が出来上がる。wowFloor.setPosition
WBoundsArea
インスタンスの位置。引数はそれぞれ、(X座標、Y座標、Z座標)を意味する。wowFloor.elasticity
WBoundsArea
の弾性。物体がぶつかってきた時に、どれくらい力を奪い取るかと言う倍率。1にすると完全弾性で、ぶつかってきたままにで返す。1以上の値を設定すると暴れまわって物体はどこかに消える。wowFloor.friction
WBoundsArea
の摩擦力。リアルにしようとするなら細かく計算するか、実際の床面などの物体に近い摩擦係数を与えてやればいい。別に0でも問題ないと思われるが、今回は気持ちを設定。- private var wowMiku:WSphere;
- private var wowRin:WSphere;
- Tweener.addTween(rin , { y:0 , time:4 , onComplete:function():void {
- wowRin = new WSphere(rin.x , rin.y , rin.z , 60 , false , 43 , 0.65);
- wow.addParticle(wowRin);
- }
- } );
- X座標
- Y座標
- Z座標
- 半径
- ぶつかったときに自分も動くかどうか?(trueだと、ぶつかられても動かない固定物体になる)
- 重さ
- 弾性
- 摩擦
- private function wowMoveModel(target:DisplayObject3D , wowTarget:WSphere , point:Number3D):void {
- var dx:Number =0;
- var dz:Number = 0;
- var rad:Number = 0;
- if (target && wowTarget && point) {
- dx = target.x - point.x; //(1)
- dz= target.z - point.z; //(1)
- rad = Math.atan2(dx , dz) * 180 / Math.PI; //(2)
- target.rotationY = rad; //(2)
- wowTarget.velocity = new WVector(-1 * dx / 4 , 0 , -1 * dz / 4);//(3)
- }
- }
target
にはミクやリンのモデルが入る。wowTarget
には、ミクやリンを表すWSphereが入る(wowMikuやwowRin)point
には、クリック位置の座標が入る- targetの現在位置とpointから、ベクトルを求める・・・(1)
- 求めたベクトルからクリック位置とモデルの角度を求め、モデルがクリック位置(目標ポイント)の方向を見るようにする。・・・(2)
なお、Metasequoia上で正面(Z方向)を向いているミクとリンのために、Math.atan2
に与える引数を工夫している wowTarget.velocity
に、求めたベクトルを元にした速度を与えてやる。・・・(3)
これにより、物体が動き出す。- wow.step();
- private function wowPositionSetting(target:DisplayObject3D , wowObj:WSphere):void {
- if (target && wowObj) {
- target.x = wowObj.px;
- target.y = wowObj.py;
- target.z = wowObj.pz;
- }
- }
で、最後にWBoundsArea
をWOWEngine
に設定して初期化は完了。
物体を追加する
物理計算を行ってくれるWOWEngine自体の初期化が完了したら、そこに物体を加えていく。今回はミクとリンの2物体を追加すればよい。初期化時の
を使う。
以下は、かぐぁみねリンが床面に降り立つ→WOWEngineの物体定義の処理を行っている部分。
2行目で物体を初期化し、3行目でその物体をWOWEngineに登録している。
WSphereというのはWOWEngine内で球体を表現するためのクラス。つまり、今回のミクとリンは見た目はミクとリンだが、物理的には球体として認識される。
WSphereの引数は全部で8つあり、先頭から
となっている。
で、作った物体を、WOWEngine#addParticle
で登録し、今後計算の対象とする。
物体を動かす
次に、物体を動かしてみる。
今回のデモだと、床をクリックした時にクリック位置に向かって突っ込んでいくように動かすわけだが、そのためには、動く方向に向かう力を与えてやればよい。力は、WSphereのメンバvelocity
に、WVector
の値を代入することで与えられる。
今回は、床クリック時に各モデルに対して以下のような関数を呼び出すようにしてみた。
各引数については、こんな感じ。
で、関数内で行っていることとしては、
このようになっている。ちなみに床はXZ平面状にあり、今回はY方向の移動を考えていないため、Yの部分は0とした。
計算する
ここまでで物体が動いている状態をシミュレートする準備が出来たので、今度はこいつを実際に動かすようにする。WOWEngineでは、メソッドWOWEngine#step()
を使って、コマ送りのように物体を動かす計算をする。なので、タイマー関数もしくはENTER_FRAME
イベントハンドラ内部に、
と言う記述を入れてやる。
表示に反映する
とまぁ、物体を動かしてやると、step()
が呼び出される毎にエンジン内の物体が動き回るわけだが、それだけでは肝心のミクとリンはピクリともしない。最初に書いたがWOWEngineは表示系と結びついてないからだ。表示、つまりミクとリンの座標位置は、こちらで適宜変更してやる必要がある。
と、言うことで、wow.step();
の実行直後に、ミクとリンに対してこんな関数を呼び出そう。
これで、ミクとリンがエリア内をガンガン跳ねながらぶつかっているようなデモになる。
雑感
全体的にはこんな感じ。ミクとリンを使って見た目は派手そうだが、その実feb19.jpのサンプルと比べると異常に簡単。オブジェクト2つだけだし。でも、フィールド上に障害物を置いたりして面白いものが作れるような気がしてきた。WOWEngineの、表示は一切やらないという態度のおかげで、人型モデルを球体扱いできるのが便利。正直、跳ね方的には殆どわからないと思う。
そう言えば、note.xさんの方では、リンが踊っていたが、あの技術を応用すれば踊ったまま衝突するミクとリンも作れそうだ。それは楽しそう。と言うか、走るとかのアニメーションが無いと、折角動いてぶつかってるのに味気ないし。
後は、衝突の瞬間にイベントが出てないかな?とかその辺を探索しよう。衝突イベント検知出来たらかなり楽しいぞ。