DebugDraw を使わない Box2DFlashAS3 のサンプル (2.0.2版)

Box2DFlashAS3 の単純なサンプルと使い方 (2.0.2版) の続き。

前回は b2DebugDraw を利用して描画をしていた。これを自前の描画に切り替えて行こう。

床の描画

床は動かないので初期化の段階で描画しちゃおう。

b2DebugDraw を初期化していた部分を床の描画に置き換える。

床はを作るときのパラメータはこんな感じだった。

// 位置:(400cm, 300cm)
// 角度:180° / 24
wallBdDef.position.Set(400 / 2 / SCALE, 300 / SCALE);
wallBdDef.angle = Math.PI / 24;

// 大きさ:360cm×20cm
wallShapeDef.SetAsBox(180 / SCALE, 10 / SCALE);

Box2D でシミュレーションするときは 1 / SCALE していた。SCALE100 なので、センチメーターをメーターに変換して Box2D に伝えていたと考えると良いだろう。

描画するときは Box2D のサイズを SCALE 倍して描画することにする。つまり、床の大きさは 360px×20px となる。

//----------------------------------
// 床を直接描画する
//----------------------------------
var floorSprite:Sprite = new Sprite();
floorSprite.graphics.beginFill(0x667788);
floorSprite.graphics.drawRect(-180, -10, 360, 20);
floorSprite.graphics.endFill();
floorSprite.x = 400 / 2;
floorSprite.y = 300;
floorSprite.rotation = 180 / 24;
addChild(floorSprite);

箱の描画

落下する箱の Sprite を生成するメソッドを作成しておく。

// 物体の Sprite を作成する
private function createSprite():Sprite{
    var ret:Sprite = new Sprite();

    // 細長い横枠を描画
    ret.graphics.lineStyle(1, 0x000000);
    ret.graphics.beginFill(0x008000);
    ret.graphics.drawRect(-40, -5, 80, 10);
    ret.graphics.endFill();

    // 画像
    var bmp:Bitmap = new Icon();
    bmp.x = bmp.y = -30;
    ret.addChild(bmp);

    // 画像の周りの線を描画
    ret.graphics.drawRect(-30, -30, 60, 60);

    return ret;
}

箱の物体を Box2D に追加したあとに m_userData に Sprite を設定して関連付けておく。

// Sprite を m_userData に割り当てる
var sprite:Sprite = createSprite();
sprite.x = sprite.y = -9999; // 画面外に移動
addChild(sprite);
objBd.m_userData = sprite;

シミュレーション処理で位置も更新する

前のサンプルでは enterFrame のイベントではシミュレーションのみを行っていたが、ここに Sprite の位置を更新する処理を追加する。

// 毎フレームの処理
var count:int = 0;
addEventListener("enterFrame", function(event:Event):void {
    // シミュレーションする
    world.Step(1 / 9, 10);

    // たまに箱を生成する
    if (count == 0 && animation){
        createObject();
    }
    count = (count + 1) % 30;

    // Sprite の場所を更新する
    for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
        var sprite:Sprite = b.GetUserData() as Sprite;
        if (sprite){
            sprite.x = b.GetWorldCenter().x * SCALE;
            sprite.y = b.GetWorldCenter().y * SCALE;
            sprite.rotation = b.GetAngle() * 180 / Math.PI;

            // 画面外に出たオブジェクトを削除する
            if (sprite.y > 600){
                world.DestroyBody(b);
                removeChild(sprite);
            }
        }
    }
});

完成品

クリックすると始まるよ。

ソースコードは以下に(165行)

package {
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import flash.events.Event;
import flash.display.*;
import flash.text.TextField;

[SWF(backgroundColor="#ccddff")]
public class Box2dSimpleSample202MyDraw extends Sprite {
    private const SCALE:Number = 10;
    private var world:b2World;

    [Embed(source="icon.png")]
    private var Icon:Class;

    // コンストラクタ
    public function Box2dSimpleSample202MyDraw() {
        stage.scaleMode = "noScale";
        stage.align = "TL";

        // メッセージとクリック時の処理
        var tf:TextField = new TextField();
        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()) {
                var sprite:Sprite = b.GetUserData() as Sprite;
                if (sprite){
                    sprite.x = b.GetWorldCenter().x * SCALE;
                    sprite.y = b.GetWorldCenter().y * SCALE;
                    sprite.rotation = b.GetAngle() * 180 / Math.PI;

                    if (sprite.y > 600){
                        world.DestroyBody(b);
                        removeChild(sprite);
                    }
                }
            }
        });
    }

    // 初期化
    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 / 2 / SCALE, 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);

        //----------------------------------
        // 床を直接描画する
        //----------------------------------
        var floorSprite:Sprite = new Sprite();
        floorSprite.graphics.beginFill(0x667788);
        floorSprite.graphics.drawRect(-180, -10, 360, 20);
        floorSprite.graphics.endFill();
        floorSprite.x = 400 / 2;
        floorSprite.y = 300;
        floorSprite.rotation = 180 / 24;
        addChild(floorSprite);
    }

    // 物体を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();

        // Sprite を m_userData に割り当てる
        var sprite:Sprite = createSprite();
        sprite.x = sprite.y = -9999; // 画面外に移動
        addChild(sprite);
        objBd.m_userData = sprite;
    }

    // 物体の Sprite を作成する
    private function createSprite():Sprite{
        var ret:Sprite = new Sprite();

        // 細長い横枠を描画
        ret.graphics.lineStyle(1, 0x000000);
        ret.graphics.beginFill(0x008000);
        ret.graphics.drawRect(-40, -5, 80, 10);
        ret.graphics.endFill();

        // 画像
        var bmp:Bitmap = new Icon();
        bmp.x = bmp.y = -30;
        ret.addChild(bmp);

        // 画像の周りの線を描画
        ret.graphics.drawRect(-30, -30, 60, 60);

        return ret;
    }
}
}