Box2DでActionScript物理プログラミング

第6回 画像を使って表現力アップ

この記事を読むのに必要な時間:およそ 4 分

その他の物体を表示する

車体の画像を読み込んで表示する

車のそれぞれのパーツについての描画方法は,床のときとほとんど同じです。まず車体を描画するためのプログラムについて説明します。

// 車体の画像を読み込んで表示する
var bodyImage:Bitmap = new BodyImage()
bodyImage.width = 0.8 * DRAW_SCALE;
bodyImage.height = 0.2 * DRAW_SCALE;
bodyImage.x = -bodyImage.width / 2;
bodyImage.y = -bodyImage.height / 2;
body.m_userData = new Sprite();
body.GetUserData().x = body.GetWorldCenter().x * DRAW_SCALE;
body.GetUserData().y = body.GetWorldCenter().y * DRAW_SCALE;
body.GetUserData().addChild(bodyImage);
addChild(body.GetUserData());

このプログラムは,車体を作り終わった直後に書きます。先ほどの床の画像を表示するためのプログラムと比較すると分かると思いますが,変化しているのは変数名と数値だけです。

変数名については,床のときにfloor~という名前になっていたものがbody~となっています。数値については,車体を作るときに設定した幅と高さ(80cmと20cm)を反映した値になっています。

車輪の画像を読み込んで表示する

この規則に従えば,前輪と後輪についても同じように書くことができます。全て書くと長くなってしまうので一部省略しますが,最後に完成形のソースコードを添付しておくので,分からないことがあったらそちらを参照してください。

// ↓前輪を作り終わった後に書く
var frontWheelImage:Bitmap = new CircleImage();
frontWheelImage.width = 0.3 * DRAW_SCALE;
frontWheelImage.height = 0.3 * DRAW_SCALE
// (略)

// ↓後輪を作り終わった後に書く
var rearWheelImage:Bitmap = new CircleImage();
rearWheelImage.width = 0.3 * DRAW_SCALE;
rearWheelImage.height = 0.3 * DRAW_SCALE;
// (略)

ここまでのソースをコンパイルして実行すると,妙なことに気づくと思います。床も車も表示はされているのですが,車の画像が宙に浮いたままです。

物理エンジンが動作していないのかというと,そういうわけではありません。DebugDrawによる描画をみれば分かることですが,きちんと動作しています。車の絵がきちんと走るようにするには,物理エンジンの情報を反映させる必要があります。最後にその方法について説明します。

物理エンジンの動作に合わせて画像を動かす

物理エンジン内の時間を進めているのは,enterFrameHandler内のworld.Stepです。これにより物理エンジン内の時間が1/24秒進みます。車の位置を物理エンジンから画面に反映するには,これと同じくenterFrameHandler内に書くとよいでしょう。

ワールド内のb2Bodyに対するfor文

物理エンジンの動作を反映させるには,enterFrameHandlerの最初に以下のようなプログラムを書きます。

for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
  // 物体の位置を更新する処理。中身は後述
}

for文の書き方が少し特殊なので説明します。world.GetBodyListは,ワールド内の最初のb2Bodyクラスの変数を返します。これがfor文における変数bの初期値となります。今回のサンプルでは床や車体などがそれに当たります。もしワールド内にb2Bodyが無ければnullが返されます。

for文の終了条件は単にbとしているので,bがnullでなければfor文の中の処理が実行されます。処理が終わると,b2BodyクラスのGetNextメソッドにより次のb2Bodyが返されます。これがnullでなければ,引き続きfor文の中の処理が実行されます。

全てのb2Bodyに対する処理が終わるとGetNextメソッドがnullを返すので,そこでfor文から抜けます。全体的なイメージとしては以下のようになります。

b = world.GetBodyList(); // for文の最初で,bは床を指す
  // 床の位置を更新する
b = b.GetNext(); // bは車体を指す
  // 車体の位置を更新する
b = b.GetNext(); // bは前輪を指す
  // 前輪の位置を更新する
b = b.GetNext(); // bは後輪を指す
  // 後輪の位置を更新する
b = b.GetNext(); // bにnullが設定され,for文を抜ける

b2Bodyの内容をSpriteに反映する

for文の中身には,以下のような処理を書きます。

// for文の中身
if (b.GetUserData() is Sprite) {
  b.GetUserData().x = b.GetWorldCenter().x * DRAW_SCALE;
  b.GetUserData().y = b.GetWorldCenter().y * DRAW_SCALE;
  b.GetUserData().rotation = b.GetAngle() * 180 / Math.PI;
}

b2BodyのユーザデータにSpriteが設定されているとき,物理エンジン内の情報をそのSpriteに反映させます。

物体の場所は,今までも何度か出てきたGetWorldCenterメソッドで得られます。これにDRAW_SCALEをかけてSpriteの座標とします。回転角度はGetAngleメソッドで取得できます。これは単位がラジアンなので,Spriteのrotationプロパティに設定するときには度に直す必要があります。

このように,b2BodyのユーザデータとしてSpriteを設定しておくと,for文を回すだけでSpriteに対する処理を一括して行えます。

DebugDrawの表示を切る

最後の仕上げに,DebugDrawによる表示を切ります。debugDraw変数を作り,それをワールドに登録している部分までを/* */でコメントアウトするだけで構いません。これでDebugDrawによる描画が行われなくなります。

完成したソースファイルとFlashがこちらです。ソースファイルは例によってクラス名を変えてあります。

 ページを開くとFlashの再生が始まり,車が動いて落ちてしまいます。車を確認するには,ページを更新してすぐにこのFlashを閲覧ください。

まとめ

画像を読み込んで表示する方法は,最初は少し面倒ですが,パターン化してしまうと簡単なものです。BitmapやSpriteを作ってb2Bodyに割り当てる部分は,メソッド化してしまうこともできます。そうすれば今回のプログラムも少しシンプルになると思います。

いったんSpriteをb2Bodyに割り当ててしまえば,表示の部分でfor文を回すだけです。それぞれのb2Bodyについて個別に処理を書く必要はありません。

次回はコンタクトリスナというものについて説明します。コンタクトとは,日本語では接触という意味です。物と物がぶつかったことを検知できる仕組みを使ったサンプルで説明しようと思います。

著者プロフィール

木村秀敬(きむらひでたか)

茨城高専,北陸先端大を卒業後,独立系ベンチャーにあこがれてjig.jpに就職。 ActionScript好きですが,根はコテコテのC/C++プログラマです。Flash/ActionScriptに興味のある方は是非Spark Projectへ。