Box2DFlashAS3を使って作る物理演算ゲーム サンプルコード
/**
* Copyright ll_koba_ll ( http://wonderfl.net/user/ll_koba_ll )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/fjRb
*/
package {
import flash.display.*;
import flash.events.*;
import flash.ui.Keyboard;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Dynamics.Joints.*;
import Box2D.Dynamics.Contacts.*;
import Box2D.Common.*;
import Box2D.Common.Math.*;
import com.flashdynamix.utils.SWFProfiler;
[SWF(frameRate=30, width=500, height=500, backgroundColor="#000000")]
public class Box2DSample01 extends Sprite {
private var _world:b2World;
private const DRAW_SCALE:Number = 100;
private var _iterations:int = 10;
private var _timeStep:Number = 1.0/30.0;
private var _debug:Boolean = false;
private var _gravity:b2Vec2;
private var _doSleep:Boolean = true;
private var _shipBody:b2Body;
private var _goalBody:b2Body;
private var _startBody:b2Body;
private var _shipView:ShipView;
private var _shipModel:ShipModel;
public function Box2DSample01() {
addEventListener(Event.ENTER_FRAME, function():void {
if (stage != null) {
removeEventListener(Event.ENTER_FRAME, arguments.callee);
init();
}
});
_shipModel = new ShipModel();
SWFProfiler.init(this);
}
private function init(e:Event = null):void {
addEventListener(Event.ENTER_FRAME, update)
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
var worldAABB:b2AABB = new b2AABB();
// 物理エンジンの範囲を決定
worldAABB.lowerBound.Set(-100, -100);
worldAABB.upperBound.Set(100, 100);
// 重力の設定
_gravity = new b2Vec2(0, 1);
// ワールドの生成
_world = new b2World(worldAABB, _gravity, _doSleep);
// デバッグ表示
if (_debug) {
var dbgDraw:b2DebugDraw = new b2DebugDraw();
var dbgSprite:Sprite = new Sprite();
addChild(dbgSprite);
dbgDraw.m_sprite = dbgSprite;
dbgDraw.m_drawScale = DRAW_SCALE;
dbgDraw.m_fillAlpha = 0.0;
dbgDraw.m_alpha = 1.0;
dbgDraw.m_lineThickness = 1.0;
dbgDraw.m_drawFlags = 0xFFFFFFFF;
_world.SetDebugDraw(dbgDraw);
}
// 各オブジェクトの生成
var stw:Number = stage.stageWidth;
var sth:Number = stage.stageHeight;
createBox("floor", stw/2, sth, stw, 10);
createBox("ceiling", stw/2, 0, stw, 10);
createBox("leftWall", 0, sth/2, 10, sth);
createBox("rightWall", stw, sth/2, 10, sth);
createBox("start", 40, sth - 10, 60, 10);
createBox("goal", stw - 40, sth - 10, 60, 10);
createBox("centerWall", stw/2, sth/2 + 90/2, 20, sth - 90);
initShip();
// 衝突判定用のリスナー設定
var cl:ContactListener = new ContactListener(this);
_world.SetContactListener(cl);
}
public function gameClear():void {
trace("gameClear");
}
public function gameOver():void {
trace("gameOver");
}
private function initShip():void {
// body生成
var bodyDef:b2BodyDef;
bodyDef = new b2BodyDef();
bodyDef.position.x = 40/DRAW_SCALE;
bodyDef.position.y = (stage.stageHeight - 40)/DRAW_SCALE;
bodyDef.userData = {};
bodyDef.userData.id = "ship";
// 表示オブジェクト設定
_shipView = new ShipView();
bodyDef.userData.displayObject = _shipView;
addChild(bodyDef.userData.displayObject);
// shape生成
var boxDef:b2PolygonDef;
boxDef = new b2PolygonDef();
boxDef.SetAsBox(40/2/DRAW_SCALE,
40/2/DRAW_SCALE);
boxDef.density = 0.1; // 密度
boxDef.friction = 0.5; // 摩擦係数
boxDef.restitution = 0.2; // 反発係数
// ワールドに追加
var body:b2Body;
body = _world.CreateBody(bodyDef);
body.CreateShape(boxDef);
body.SetMassFromShapes();
_shipBody = body;
}
private function createBox(id:String, x:Number, y:Number, w:Number, h:Number):void {
// body生成
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.x = x/DRAW_SCALE;
bodyDef.position.y = y/DRAW_SCALE;
bodyDef.userData = {};
bodyDef.userData.id = id;
// 表示オブジェクト作成
var display:Sprite = new Sprite();
var g:Graphics = display.graphics;
g.lineStyle(1, 0xFFFFFF);
g.drawRect(-w/2,-h/2,w,h);
addChild(display);
bodyDef.userData.displayObject = display;
// Shape生成
var boxDef:b2PolygonDef = new b2PolygonDef();
boxDef.SetAsBox(w/2/DRAW_SCALE, h/2/DRAW_SCALE);
boxDef.density = 0; // 固定されたオブジェクトに
// ワールドに追加
var body:b2Body = _world.CreateBody(bodyDef);
body.CreateShape(boxDef);
}
private function update(event:Event):void {
if (_world == null) return;
_world.Step(_timeStep, 10);
for (var bb:b2Body = _world.m_bodyList; bb; bb = bb.m_next) {
updateBodys(bb);
// m_userDataに表示オブジェクトが入っている場合は、表示オブジェクトに計算結果を反映
if (bb.m_userData) {
if (bb.m_userData.displayObject is Sprite) {
bb.m_userData.displayObject.x = bb.GetPosition().x * DRAW_SCALE;
bb.m_userData.displayObject.y = bb.GetPosition().y * DRAW_SCALE;
bb.m_userData.displayObject.rotation = bb.GetAngle() * (180/Math.PI);
if(bb.m_userData.displayObject.y > stage.stageHeight ) {
removeChild(bb.m_userData.displayObject);
_world.DestroyBody(bb);
}
}
}
}
// 宇宙船を更新
var a:Number = ((_shipBody.GetAngle() * 180/Math.PI) - 90) * Math.PI/180;
var vx:Number = Math.cos(a) * _shipModel.power;
var vy:Number = Math.sin(a) * _shipModel.power;
var v:b2Vec2 = _shipBody.GetLinearVelocity();
if (_shipModel.power > 0) {
_shipBody.SetLinearVelocity(new b2Vec2(v.x + vx, v.y + vy));
}
_shipBody.SetAngularVelocity(_shipModel.vr * Math.PI/180);
}
private function updateBodys(body:b2Body):void {
}
private function keyDownHandler(e:KeyboardEvent):void {
// shipBodyがsleepしてる可能性があるので
_shipBody.WakeUp();
switch(e.keyCode) {
case Keyboard.UP:
_shipView.showFire();
_shipModel.power = 0.1;
break;
case Keyboard.LEFT:
_shipModel.vr = -50;
break;
case Keyboard.RIGHT:
_shipModel.vr = 50;
break;
}
}
private function keyUpHandler(e:KeyboardEvent):void {
switch(e.keyCode) {
case Keyboard.UP:
_shipView.hideFire();
_shipModel.power = 0;
break;
case Keyboard.LEFT:
case Keyboard.RIGHT:
_shipModel.vr = 0;
break;
}
}
}
}
import flash.display.*;
class ShipModel {
public var power:Number = 0;
public var vr:Number = 0;
}
class ShipView extends Sprite {
private var _fire:Sprite;
public function ShipView() {
var g:Graphics = graphics;
g.lineStyle(1, 0xFFFFFF);
g.drawRect(-20,-20, 40, 40);
_fire = new Sprite();
g = _fire.graphics;
g.lineStyle(1, 0xFF0000);
g.moveTo(-10, 0);
g.lineTo(0, 20);
g.lineTo(10, 0);
g.lineTo(-10, 0);
_fire.y = 23;
_fire.visible = false;
addChild(_fire);
}
public function showFire():void {
_fire.visible = true;
}
public function hideFire():void {
_fire.visible = false;
}
}
import Box2D.Collision.Shapes.*;
import Box2D.Collision.b2ContactPoint;
import Box2D.Dynamics.b2ContactListener;
import Box2D.Dynamics.*;
class ContactListener extends b2ContactListener {
private var _gameStage:Box2DSample01;
public function ContactListener(gameStage:Box2DSample01) {
_gameStage = gameStage;
}
public override function Add(point:b2ContactPoint):void
{
var s1:b2Shape = point.shape1;
var s2:b2Shape = point.shape2;
var b1:b2Body = s1.m_body;
var b2:b2Body = s2.m_body;
if (b1.m_userData == null && b2.m_userData == null) return;
// b1が宇宙船の場合
if (b1.m_userData != null && b1.m_userData.id == "ship") {
if (b2.m_userData.id != "start" &&
b2.m_userData.id != "goal") {
_gameStage.gameOver();
} else if (b2.m_userData.id == "goal") {
_gameStage.gameClear();
}
}
// b2が宇宙船の場合
if (b2.m_userData != null && b2.m_userData.id == "ship") {
if (b1.m_userData.id != "start" &&
b1.m_userData.id != "goal") {
_gameStage.gameOver();
} else if (b1.m_userData.id == "goal") {
_gameStage.gameClear();
}
}
}
}