The Box2d Physics Engine for ActionScript from the Ground Up
We’ll start with the basic 2d environment (part 1) and work our way into creating dynamic polygons (part 2) with an anti-gravity effect (part 3).
Here’s a preview of what we will end up with:
Google Maps Text Event
(click and hold the mouse down for anti-gravity)
What is Box2d?
It’s a physics engine that was written in C++ and ported over to ActionScript. Learn more about it here.
Now what?
Download the framework here. Start a new ActionScript project and put the Box2D folder into your src directory.
Set up some constants.
Boring! I know, but you’ll be happy later Luckily they are all commented so you know what effect they will have when we start adding objects. You’ll notice the PHYS_SCALE variable. The physics environment is not one to one with the stage in Flash. This scale is used to translate coordinates between the two environments.
// Number of pixels in a Meter (Usually between 10 and 30).
// The closer to 1, the more accurate. And more processor intensive.
public static const PHYS_SCALE:Number = 10;
// Restitution is used to make objects bounce (usually set to be between 0 and 1).
// 0 means the ball won't bounce. 1 means the ball's velocity will be exactly reflected.
public static const RESTITUTION:Number = 0.7;
// Friction is used to make objects slide along each other (usually set to be between 0 and 1).
// 0 turns off friction and a value of one makes the friction strong.
public static const FRICTION:Number = 0.6;
// How many physical calculations per timestep.
// Usually 1 / 15 or 1 / 60.
public static const TIMESTEP:Number = 1.0 / 15.0;
// The suggested iteration count is 10.
public static const ITERATIONS:Number = 10.0;
Create a base 2d environment.
Starting simple. We need a world. The upper bound and lower bound are in the physics scale and should be larger than your current stage. Objects will stay alive as long as they are in the bounds of the world. Right now that is 100 * 10 (our physics scale) = 1000 pixels positive and negative.
protected var world:b2World;
public function Base2dEnvironment()
{
var gravity:b2Vec2 = new b2Vec2(0,9.8);
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(-100,-100);
worldAABB.upperBound.Set(100,100);
world = new b2World(worldAABB, gravity, true);
}
We also need a way to kickstart our world.
protected var running:Boolean = false;
public function start():void
{
if(!running){
addEventListener(Event.ENTER_FRAME, update, false, 0, true);
running = true;
}
}
public function pause():void
{
if(running){
removeEventListener(Event.ENTER_FRAME, update);
running = false;
}
}
protected function update(e:Event):void {
world.Step(timestep, iterations);
}
Now it’s time to add objects right? Well, first we want some boundaries so our objects don’t fall out of our display area. And how about a debug mode so we can actually see our invisible boundaries?
protected var box_width:int;
protected var box_height:int;
protected var debug_mode:Boolean;
protected var margin:int = 5;
public function Base2dEnvironment(boxWidth:int = 800, boxHeight:int = 500, debugMode:Boolean = false)
{
// ...(world code from above)... //
box_width = boxWidth;
box_height = boxHeight;
debug_mode = debugMode;
//Shows boundries (green transparency) used to see 'Ground'
if(debugMode){
drawGroundBoundries();
}
// Bottom wall
AddStaticBox((box_width / 2)/Constants.PHYS_SCALE, (box_height + margin)/Constants.PHYS_SCALE, (box_width / 2) / Constants.PHYS_SCALE, margin / Constants.PHYS_SCALE);
// Top wall
AddStaticBox((box_width / 2)/Constants.PHYS_SCALE, -margin / Constants.PHYS_SCALE, (box_width / 2) / Constants.PHYS_SCALE, margin / Constants.PHYS_SCALE);
// Left wall
AddStaticBox(-margin / Constants.PHYS_SCALE,(box_height / 2) / Constants.PHYS_SCALE, margin / Constants.PHYS_SCALE,(box_height / 2 ) / Constants.PHYS_SCALE);
// Right wall
AddStaticBox((box_width + margin) / Constants.PHYS_SCALE,(box_height / 2)/Constants.PHYS_SCALE, margin / Constants.PHYS_SCALE,((box_height) / 2) / Constants.PHYS_SCALE);
}
private function drawGroundBoundries():void
{
var m_sprite:Sprite;
m_sprite = new Sprite();
addChild(m_sprite);
var dbgDraw:b2DebugDraw = new b2DebugDraw();
var dbgSprite:Sprite = new Sprite();
m_sprite.addChild(dbgSprite);
dbgDraw.m_sprite = m_sprite;
dbgDraw.m_drawScale = Constants.PHYS_SCALE;
dbgDraw.m_alpha = 1;
dbgDraw.m_fillAlpha = 0.5;
dbgDraw.m_lineThickness = 1;
dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
world.SetDebugDraw(dbgDraw);
}
// Used for drawing the ground
private function AddStaticBox(_x:Number,_y:Number,_halfwidth:Number,_halfheight:Number):void {
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(_x, _y);
var boxDef:b2PolygonDef = new b2PolygonDef();
boxDef.SetAsBox(_halfwidth, _halfheight);
boxDef.density = 0.0;
var body:b2Body = world.CreateBody(bodyDef);
body.CreateShape(boxDef);
body.SetMassFromShapes();
}
Now we have our world! Let’s start adding objects. Click here for part 2.