2007年11月29日
Box2DFlashAS3 の見た目を豪華にしてみる
前回 のサンプルを改造して、見た目に一味加えてみた。
ちょっぴり実写志向。(表示されない場合はリロードをお願いします)
原理は簡単。Box2dFlashAS3 は、もともとが C++ のコードなだけに、エンジンに描画処理が含まれていない。前回も自前で lineTo を使って描画してたのだけど、今回は事前に Sprite を用意しておいた。enterFrame で b2Body の位置と角度を Sprite に反映させている。
ただ、物理エンジンの初期化と Sprite の描画の両方を AS3 で書いているので、ソースコードが無駄に長い。もうちょっと複雑になるときは、しっかり設計したほうがよさそうだ。
だるまの絵は以下のサイトから借用させていただいた。無駄にアフィリエイト(笑)。
だるま落とし |
ソースコードは以下に(139行)。
(追記)1.4.3 では import Engine のところを import Box2d に変更すれば動きます。
package {
import Engine.Dynamics.*;
import Engine.Collision.*;
import Engine.Collision.Shapes.*;
import Engine.Common.Math.*;
import flash.events.Event
import flash.display.*;
import flash.text.TextField;
[SWF(backgroundColor="#ffffff", width="350", height="200")]
public class Box2dSimpleSample2 extends Sprite {
private var count:int = 0; // loop counter
private var first:Boolean = true; // init flag
// Box2d
private var m_world:b2World;
private var m_physScale:Number = 10;
private var m_floor:b2Body;
private var m_blocks:Array;
// Sprites
private var spriteFloor:Sprite;
private var spriteBlocks:Array;
[Embed(source="daruma.png")]
private var daruma:Class;
private const colors:Array = [0x333366, 0xffffdd, 0xcc0000, 0xffcc00, 0x006600];
// display param
private static const LOOP:int = 280;
private static const FADE_OUT:int = 20;
public function Box2dSimpleSample2() {
stage.scaleMode = "noScale";
stage.align = "TL";
var text:TextField = new TextField();
text.text = "CLICK TO START!!!";
text.x = text.y = 100;
addChild(text);
stage.addEventListener("click", function(event:Event):void {
text.visible = false;
init();
});
}
private function init():void {
count = 0;
// init sprites
if(first){
first = false;
// init floor sprite
spriteFloor = new Sprite();
spriteFloor.graphics.beginFill(0x808080);
spriteFloor.graphics.drawRect(-300 / 2, -20 / 2, 300, 20);
spriteFloor.graphics.endFill();
addChild(spriteFloor);
// init blocks sprite
spriteBlocks = [];
for(i = 0; i < colors.length; i++) {
var s:Sprite = new Sprite();
s.graphics.beginFill(colors[i]);
s.graphics.lineStyle(0, 0x999999);
s.graphics.drawRoundRect(-28, -10, 56, 20, 10, 10);
s.graphics.endFill();
addChild(s);
spriteBlocks.push(s);
}
s = new Sprite();
var bmp:DisplayObject = s.addChild(new daruma());
bmp.x = -bmp.width / 2;
bmp.y = -bmp.height / 2;
addChild(s);
spriteBlocks.push(s);
addEventListener("enterFrame", function(event:Event):void {
Update();
});
}
// Construct a world object
var worldAABB:b2AABB = new b2AABB();
worldAABB.minVertex.Set(-100.0, -100.0);
worldAABB.maxVertex.Set(100.0, 100.0);
var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);
m_world = new b2World(worldAABB, gravity, true);
// Create floor
var wallSd:b2BoxDef = new b2BoxDef();
wallSd.extents.Set(300 / 2 / m_physScale, 20 / 2 / m_physScale);
var wallBd:b2BodyDef = new b2BodyDef();
wallBd.position.Set(300 / m_physScale / 2, 250 / m_physScale);
wallBd.rotation = Math.random() * Math.PI / 8;
wallBd.AddShape(wallSd);
m_floor = m_world.CreateBody(wallBd);
// Add bodies
var sd:b2BoxDef = new b2BoxDef();
sd.density = 1;
sd.friction = 0.2;
var bd:b2BodyDef = new b2BodyDef();
bd.AddShape(sd);
m_blocks = [];
for (var i:int = 0; i < spriteBlocks.length; i++) {
sd.extents.Set(30 / m_physScale, (i == 5 ? 30 : 10) / m_physScale);
bd.position.Set(100 / m_physScale, (120 - i * 20 - (i == 5 ? 18 : 0)) / m_physScale);
m_blocks.push(m_world.CreateBody(bd));
}
}
public function Update():void {
count++;
if(count > 300) {
init();
}
// Update physics
m_world.Step(1 / 30, 10);
// Render
var alpha:Number = (count > LOOP - FADE_OUT ? (LOOP - count) / FADE_OUT :
count < FADE_OUT ? count / FADE_OUT : 1);
applyBodyToSprite(spriteFloor, m_floor, alpha);
for(var i:int = 0; i < m_blocks.length; i++) {
applyBodyToSprite(spriteBlocks[i], m_blocks[i], alpha);
}
}
private function applyBodyToSprite(sprite:Sprite, body:b2Body, alpha:Number):void{
sprite.x = body.m_position.x * m_physScale;
sprite.y = body.m_position.y * m_physScale;
sprite.rotation = body.m_rotation * 180 / Math.PI;
sprite.alpha = alpha;
}
}
}