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

第3回 マウスのドラッグ&ドロップで,好きなサイズの箱を作る

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

マウスを使った箱の作成

マウスをドラッグして箱を作るプログラムは,改良版DropBoxのときに説明したことの応用でできます。

ドラッグ開始の処理

マウスのボタンが押されたら,その場所をboxBeginに記憶しておきます。

private function mouseDownHandler(event:MouseEvent):void {
	// マウスが押された場所を記憶しておく
	boxBegin.x = event.stageX;
	boxBegin.y = event.stageY;
}

boxBeginはPoint型の変数です。この時点ではこの場所が箱の四隅のどこになるかは分かりませんが,ひとまず記憶しておきます。

ドラッグ終了の処理

マウスのボタンが離されたら,箱の場所と大きさを計算して設置します。大まかな流れを最初に示します。

private function mouseUpHandler(event:MouseEvent):void {
	// 箱の場所と大きさを計算する
	
	// 小さすぎる箱ができるのを防ぐ
	
	// 箱の場所を設定する
	
	// 箱の大きさなどを設定する
	
	// 箱を動く物体として作る
}

箱の場所と大きさを計算する

まずは箱の場所と大きさの計算です。分かりやすいように図解します。

箱の場所と大きさ

箱の場所と大きさ

xとyが箱の中心座標,halfWidthとhalfHeightが箱の幅と高さの半分の値です。なぜわざわざ高さと幅を半分にしているかというと,最終的にBox2Dの機能を呼び出すときには半分の値が必要になるからです。これらの値を計算するプログラムは以下のようになります。

var x:Number = (event.stageX + boxBegin.x) / 2 / DRAW_SCALE;
var y:Number = (event.stageY + boxBegin.y) / 2 / DRAW_SCALE;
var halfWidth:Number = Math.abs(event.stageX - boxBegin.x) / 2 / DRAW_SCALE;
var halfHeight:Number = Math.abs(event.stageY - boxBegin.y) / 2 / DRAW_SCALE;

stageX,stageY,boxBeginに入っている値はカーソル座標のままで,範囲が500x375の中となっています。これをDRAW_SCALEで割ることで,物理エンジン内の座標に変換することができます。DRAW_SCALEはb2DebugDrawにも使っている値で,以下のように定義されています。今回は何ヶ所かでこの数値を使うので,このように定数にしています。

private static const DRAW_SCALE:Number = 100;

小さすぎる箱ができるのを防ぐ

箱の大きさを計算し,幅か高さが10cm以内になってしまった場合は,箱を作らずに処理を終了します。なぜこのような処理を行うかというと,あまりにも箱が小さいとシミュレーションが狂ってしまうからです。この処理を行っているのが以下のコードです。

if (halfWidth < 0.05 || halfHeight < 0.05) {
	return;
}

このif文を外して小さな箱を作れるようにすると,どのようにシミュレーションが狂ってしまうのかを試すことができます。上のほうから小さな箱を落とすと,下にある箱を貫通してしまうことがあります。このような不具合を避けるために,あらかじめ条件式を設けることで,小さい箱が作れないようにしています。

箱の場所や大きさなどを設定する

次に箱の場所や大きさなどを設定します。ここでも前回のおさらいですが,b2BodyDefで場所を,b2PolygonDefで大きさや密度などを設定します。

箱の場所は,先ほど計算したxとyをそのまま使います。前回から変わった点といえば,Setの引数が固定された値から変数になった所だけです。

var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(x, y);

箱の大きさにはhalfWidthとhalfHeightを使います。今回は箱を傾けてしまうとかえって分かりにくくなると思ったので,SetAsOrientedBoxではなくSetAsBoxを使っています。

var shapeDef:b2PolygonDef= new b2PolygonDef();
shapeDef.SetAsBox(halfWidth, halfHeight);
shapeDef.density = 1;     // 密度 [kg/m^2]
shapeDef.restitution = 0;  // 反発係数、通常は0~1

密度は前回から変えていませんが,反発係数は0にしています。今回のように箱などの物体がたくさん置かれるようなケースでは,反発係数の値を大きくしてしまうと変な動きをしてしまう場合があります。0にするのはそういう事態を防ぐためです。

箱を作る

最後に,これらの情報を基に箱を作ります。ここは前回とまったく同じです。

var body:b2Body = world.CreateDynamicBody(bodyDef);
body.CreateShape(shapeDef);
body.SetMassFromShapes();

物理エンジンの初期化は,Tsumikiクラスのコンストラクタ内で行っています。また,床の設置やDebugDrawの設定についても同じくコンストラクタで行っています。内容は前回と同じで,clickHandlerからコンストラクタに移動しただけなので,説明は割愛します。

まとめ

マウスのドラッグ&ドロップによって箱を作るFlashを作りながら,マウスカーソルの座標と物理エンジン内の座標の関係について説明してきました。この関係が理解できれば,Box2Dを使ってできることがかなり広がると思います。

次回はこれを応用し,長方形だけではなく,円や三角形なども作れるようにしてみたいと思います。

著者プロフィール

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

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