Box2DFlashAS3 の単純なサンプルと使い方 (2.0.2版)
Box2dFlashAS3 はバージョンによって API が激しく変わっていてなかなか困りものだ。2.0.0 なら gihyo.jp の 特集:Box2DでActionScript物理プログラミング が分かりやすくてよいんだけど、2.0.1 で重要な API が改名されていて、そのことがパッケージには書いていない。
「Box2dFlashAS3 どうなってるんだ」と思ったら、どうやら Box2D 本家の API 変更に素直に追従しているだけのようだ。本家側ではそこそこドキュメントはそろっているようなので、ドキュメントが欲しい人は Box2D 付属のクラスライブラリや Box2D User Manual を見たほうがよいかもしれない。
今回は Box2dFlashAS3 ver 2.0.2 の単純なサンプルを作った。クリックすると始まるよ。
これのソースコードを紹介しながら Box2dFlashAS3 を使う手順を解説していく。
目次:
- 世界の作成
- 床の作成
- 落下する物体の作成
- シミュレーションの開始
- 描画
1. 世界の作成
シミュレーションを行うための世界を作成する。
// シミュレーションする座標の範囲を指定する var worldAABB:b2AABB = new b2AABB(); worldAABB.lowerBound.Set(-100.0, -100.0); worldAABB.upperBound.Set(100.0, 100.0); // 重力を定義する var gravity:b2Vec2 = new b2Vec2(0.0, 10.0); // 世界のインスタンスを作成する world = new b2World(worldAABB, gravity, true);
まずは、worldAABB を作ってシミュレーション計算する範囲を指定する。*1
次に重力を設定する。y 軸正方向に 10.0。物理をかじったことある人にはおなじみの値(正確には 9.80665… [m/s2])。
これで準備完了。世界を生成する。
m_world = new b2World(worldAABB, gravity, true);
第3パラメータは doSleep。ここを true にしておくと、物体が動いていないときはその分のシミュレーション処理を削減できる。
2. 床の作成
さて、世界ができたら床を作っていこう。
ここから世界に物体を配置していくんだけど、Box2D では、円や箱を作る手順がちょっと複雑なので毎回忘れてしまう。始めに手順を整理しておく。
物体を作る方法
- 物体の定義を作る
new b2BodyDef()- 場所や角度を決める
- 物体を作る
world.CreateBody(bodyDef):b2Body- 世界上に物体を作成する
- 形の定義を作る
b2PolygonDef, b2CircleDef- 形の大きさなどを決める
- 反発係数、摩擦係数などを決める
- 形を物体に追加する
body.CreateShape(shapeDef)- 物体上に形を追加する
- いくつもの形を追加してもよい
- (動く物体の場合のみ) 重さを計算する
body.SetMassFromShapes()- これを忘れると落ちない
XXXDef というのは XXX を作成するときのパラメータだと考えるとよいだろう。事前に XXXDef に値を設定しておいて、CreateXXXX(XXXDef); メソッドで実体を作る、という流れになる。
それでは、手順に沿って作成していこう。
2-1. 物体の定義を作る。
// 物体の定義を作る var wallBdDef:b2BodyDef = new b2BodyDef(); wallBdDef.position.Set(400 / SCALE / 2, 300 / SCALE); wallBdDef.angle = Math.PI / 24;
400, 300 の位置に物体を作る。角度はラジアンで指定する(π(=180°) / 24 なので 7.5°傾ける)。
2-2. 物体を作る。
世界上に物体を作成する。
// 物体を作る var wallBd:b2Body = world.CreateBody(wallBdDef);
2-3. 形の定義を作成する
// 形の定義を作る var wallShapeDef:b2PolygonDef = new b2PolygonDef(); wallShapeDef.SetAsBox(180 / SCALE, 10 / SCALE);
b2PolygonDef を使って 180×10 の四角形の定義を作る。*2
2-4. 形を物体に追加する
定義を作っただけではだめで物体に追加しなきゃいけない。
// 形を物体に追加する wallBd.CreateShape(wallShapeDef);
b2Body オブジェクトの CreateShape を呼ぶ。*3
2-5. (動く物体の場合のみ) 重さを計算する
今回は動かない物体なので必要ない。
3. 落下する物体の作成
同じようにして積み木を作成していく。
// 物体の定義を作る (x 座標と角度はランダム) var objBdDef:b2BodyDef = new b2BodyDef(); objBdDef.position.Set((300 * Math.random()) / SCALE, 0); objBdDef.angle = Math.PI / 2 * Math.random(); // 物体を作る var objBd:b2Body = world.CreateBody(objBdDef); // 形の定義を作る var shapeDef:b2PolygonDef = new b2PolygonDef(); shapeDef.SetAsBox(30 / SCALE, 30 / SCALE); shapeDef.density = 1; shapeDef.restitution = 0.4; shapeDef.friction = 0.1; // 形を物体に追加する objBd.CreateShape(shapeDef); // 定義を変更してもう1個の形を追加する shapeDef.SetAsBox(40 / SCALE, 5 / SCALE); objBd.CreateShape(shapeDef); // 重さ・重心を計算する objBd.SetMassFromShapes();
先ほどの手順と同じだが、動く物体なので最後に SetMassFromShapes() を利用して重さや重心を計算している。
ShepeDef のプロパティは次の通り。
density0以外に設定すると、固定されない(自由落下する)ようになる。restitution- はね返り具合。
friction- 摩擦ぐあいを表すパラメータ。
0だとよく滑る。
物体にはいくつも形を追加できる。ここでは、2個の四角を追加している。
4. シミュレーションの開始
enterFrame の中で次の関数を実行する。
world.Step(1 / 9, 10);
- 第一引数(
timeStep)は1回のステップで進める秒数。 - 第二引数(
iterations)はシミュレーションの精度。小さいとパフォーマンスがよくなり、大きいと正確になる(めり込んだりしない)
5. 描画
ver 2 から DebugView が導入された。開発の初期の段階では、物体を正しく定義できているか調べるのに便利だ。今回も DebugView を使って描画している。
一回、次のように定義するだけで、自動的に描画してくれるようになる。ありがたい。
//---------------------------------- // DebugDraw を有効にする //---------------------------------- var debugDraw:b2DebugDraw = new b2DebugDraw(); debugDraw.m_sprite = this; debugDraw.m_drawScale = SCALE; debugDraw.m_fillAlpha = .8; debugDraw.m_lineThickness = 1; debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit; world.SetDebugDraw(debugDraw);
ソース
コメントを細かく書いているので128行あります。
package {
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import flash.events.Event
import flash.display.Sprite;
import flash.text.TextField;
[SWF(backgroundColor="#666666", width="350", height="200")]
public class Box2dSimpleSample202 extends Sprite {
private const SCALE:Number = 10;
private var world:b2World;
// コンストラクタ
public function Box2dSimpleSample202() {
stage.scaleMode = "noScale";
stage.align = "TL";
// メッセージとクリック時の処理
var tf:TextField = new TextField();
tf.textColor = 0xffffff;
tf.text = "Click to start";
addChild(tf);
var animation:Boolean = false;
stage.addEventListener("click", function(event:Event):void{
count = 0;
animation = !animation;
tf.text = "Click to " + (animation ? "stop" : "start");
});
// 初期化
init();
createObject();
// 毎フレームの処理
var count:int = 0;
addEventListener("enterFrame", function(event:Event):void {
world.Step(1 / 9, 10);
if (count == 0 && animation){
createObject();
}
count = (count + 1) % 30;
// 下に行ったオブジェクトを削除する
for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
if (b.GetWorldCenter().y * SCALE > 600){
world.DestroyBody(b);
}
}
});
}
// 初期化
private function init():void {
//----------------------------------
// 世界を作成する
//----------------------------------
// シミュレーションする座標の範囲を指定する
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(-100.0, -100.0);
worldAABB.upperBound.Set(100.0, 100.0);
// 重力を定義する
var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);
// 世界のインスタンスを作成する
world = new b2World(worldAABB, gravity, true);
//----------------------------------
// 床を作る
//----------------------------------
// 物体の定義を作る
var wallBdDef:b2BodyDef = new b2BodyDef();
wallBdDef.position.Set(400 / SCALE / 2, 300 / SCALE);
wallBdDef.angle = Math.PI / 24;
// 物体を作る
var wallBd:b2Body = world.CreateBody(wallBdDef);
// 形の定義を作る
var wallShapeDef:b2PolygonDef = new b2PolygonDef();
wallShapeDef.SetAsBox(180 / SCALE, 10 / SCALE);
// 形を物体に追加する
wallBd.CreateShape(wallShapeDef);
//----------------------------------
// DebugDraw を有効にする
//----------------------------------
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.m_sprite = this;
debugDraw.m_drawScale = SCALE;
debugDraw.m_fillAlpha = .8;
debugDraw.m_lineThickness = 1;
debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
world.SetDebugDraw(debugDraw);
}
// 物体を1個作る
private function createObject():void{
// 物体の定義を作る (x 座標と角度はランダム)
var objBdDef:b2BodyDef = new b2BodyDef();
objBdDef.position.Set((300 * Math.random()) / SCALE, 0);
objBdDef.angle = Math.PI / 2 * Math.random();
// 物体を作る
var objBd:b2Body = world.CreateBody(objBdDef);
// 形の定義を作る
var shapeDef:b2PolygonDef = new b2PolygonDef();
shapeDef.SetAsBox(30 / SCALE, 30 / SCALE);
shapeDef.density = 1;
shapeDef.restitution = 0.4;
shapeDef.friction = 0.1;
// 形を物体に追加する
objBd.CreateShape(shapeDef);
// 定義を変更してもう1個の形を追加する
shapeDef.SetAsBox(40 / SCALE, 5 / SCALE);
objBd.CreateShape(shapeDef);
// 重さ・重心を計算する
objBd.SetMassFromShapes();
}
}
}