Box2D 2.1a Tutorial – Part 1
Physics engines are arguably one of the most engaging elements in interactive media. Not only do they add visual realism but they also allow a greater interactive experience.
The Flash platform has a multitude of physics engine libraries available but the one I will be focusing on is Box2D 2.1a - arguably the most powerful and feature rich physics engine of them all.
A little background info: Box2D is the brainchild of Erin Catto, Blizzard‘s Principle Software Engineer. Box2D is in fact written in C++, but such is its popularity it spawned many ports with ActionScript 3 being no exception. In charge of the manual porting to ActionScript 3 has been BorisTheBrave who has kept up with Erin’s updates. With the release of 2.1 emerged a new port, created by Jesse Sternberg, who leveraged Alchemy to generate AS3 code from the C++ version. He has also made a framework WCK to allow developers to create Box2D objects by using the Flash IDE to layout the various elements.
However, for now, I will be focusing on BorisTheBrave’s port which you can download from here.
Box2D can be fairly intimidating to begin developing with due to its sheer scale so we are going to ease into it. I have based my following tutorial heavily on Box2D manual’s Hello World tutorial, the major changes being all the code examples are written in AS3 and a few minor creative liberties that I have taken. Make sure you are familiar with the following definitions from the Box2D manual:
- shape: A 2D geometrical object, such as a circle or polygon.
- rigid body: A chunk of matter that is so strong that the distance between any two bits of matter on the chunk is completely constant. They are hard like a diamond. In the following discussion we use body interchangeably with rigid body.
- fixture: A fixture binds a shape to a body and adds material properties such as density, friction, and restitution.
- world: A physics world is a collection of bodies, fixtures, and constraints that interact together. Box2D supports the creation of multiple worlds, but this is usually not necessary or desirable.
To help visualize the relationship between the classes I have created this chart:
Lets start coding:
1. Create the world. The first parameter is a 2D vector that defines the world’s gravity. The second parameter is a Boolean to allow bodies to sleep. This is an optimization so that the physics engine does not needlessly process objects in the world that are not moving.
package
{
import Box2D.Dynamics.b2World;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2Body;
import Box2D.Collision.Shapes.b2PolygonShape;
import Box2D.Dynamics.b2Fixture;
import Box2D.Dynamics.b2FixtureDef;
import flash.display.MovieClip;
import Box2D.Dynamics.b2DebugDraw;
import flash.display.Sprite;
import flash.events.Event;
public class HelloWorld extends MovieClip
{
private const PIXELS_TO_METRE:int = 30;
private const SWF_HALF_WIDTH:int = 400;
private const SWF_HEIGHT:int = 600;
private var _world:b2World;
public function HelloWorld()
{
_world = new b2World(new b2Vec2(0,10),true);
2. Create the ground box definition. In Box2D the units of measurement are in Metres. We specify a ratio of pixels to metres (this value is up to you). Additionally, the objects in Box2D have a origin that is in the centre of the object. Since the SWF in my example is 800×600 and the ground box will be 800 pixels wide to match, I set the X position to half that and convert to metres.
var groundBodyDef:b2BodyDef= new b2BodyDef();
groundBodyDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METRE,
SWF_HEIGHT/PIXELS_TO_METRE-20/PIXELS_TO_METRE);
3. Here we can see the factory design pattern being used to create the body.
var groundBody:b2Body = _world.CreateBody(groundBodyDef);
4. Now we create our shape.
var groundBox:b2PolygonShape = new b2PolygonShape(); groundBox.SetAsBox(SWF_HALF_WIDTH/PIXELS_TO_METRE,20/PIXELS_TO_METRE);
5. We can set various properties when we create the fixture.
var groundFixtureDef:b2FixtureDef = new b2FixtureDef(); groundFixtureDef.shape = groundBox; groundFixtureDef.density = 1; groundFixtureDef.friction = 1; groundBody.CreateFixture(groundFixtureDef);
The following code runs through steps 1-5 again as we make a small box to place in this world.
var bodyDef:b2BodyDef = new b2BodyDef(); bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METRE,4); var body:b2Body = _world.CreateBody(bodyDef); var dynamicBox:b2PolygonShape = new b2PolygonShape(); dynamicBox.SetAsBox(1,1); var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.shape = dynamicBox; fixtureDef.density = 1; fixtureDef.friction = 0.3; body.CreateFixture(fixtureDef);
6. This part I added, you won’t find it on the HelloWorld tutorial from the Box2D tutorial. This piece of code allows us to see the world we have created rather than just trace statements.
var debugSprite:Sprite = new Sprite(); addChild(debugSprite); var debugDraw:b2DebugDraw = new b2DebugDraw(); debugDraw.SetSprite(debugSprite); debugDraw.SetDrawScale(PIXELS_TO_METRE); debugDraw.SetLineThickness( 1.0); debugDraw.SetAlpha(1); debugDraw.SetFillAlpha(0.4); debugDraw.SetFlags(b2DebugDraw.e_shapeBit); _world.SetDebugDraw(debugDraw);
7. Now we need to give this world some life and let it run. An ENTER_FRAME event listener will do the job.
addEventListener(Event.ENTER_FRAME, update); }
8. To update the world we call the step function. Step accepts three parameters. The first being the timestep. Box2D manual recommends 1/60 seconds. I find it best to set it to the SWF framerate , so in this case 1/30. The next two parameters are the velocityIterations and positionIteration with 10 being the suggested count for each. Fewer iterations boosts performances but comes at the cost of accuracy.
public function update(e : Event):void
{
var timeStep:Number = 1 / 30;
var velocityIterations:int = 6;
var positionIterations:int = 2;
_world.Step(timeStep,velocityIterations,positionIterations);
9. As of version 2.1 we must clear the forces.
_world.ClearForces();
10. For drawing the debug data that we set earlier.
_world.DrawDebugData(); }
So that ends the first Box2D 2.1a tutorial. If you decide to start experimenting on your own and are checking out other code samples be careful of what version you are looking at. Pre 2.1 Box2D code examples will not compile without tweaking. Final code:
package
{
import Box2D.Dynamics.b2World;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2Body;
import Box2D.Collision.Shapes.b2PolygonShape;
import Box2D.Dynamics.b2Fixture;
import Box2D.Dynamics.b2FixtureDef;
import flash.display.MovieClip;
import Box2D.Dynamics.b2DebugDraw;
import flash.display.Sprite;
import flash.events.Event;
public class HelloWorld extends MovieClip
{
private const PIXELS_TO_METRE:int = 30;
private const SWF_HALF_WIDTH:int = 400;
private const SWF_HEIGHT:int = 600;
private var _world:b2World;
public function HelloWorld()
{
_world = new b2World(new b2Vec2(0,10),true);
var groundBodyDef:b2BodyDef= new b2BodyDef();
groundBodyDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METRE,
SWF_HEIGHT/PIXELS_TO_METRE-20/PIXELS_TO_METRE);
var groundBody:b2Body = _world.CreateBody(groundBodyDef);
var groundBox:b2PolygonShape = new b2PolygonShape();
groundBox.SetAsBox(SWF_HALF_WIDTH/PIXELS_TO_METRE,
20/PIXELS_TO_METRE);
var groundFixtureDef:b2FixtureDef = new b2FixtureDef();
groundFixtureDef.shape = groundBox;
groundFixtureDef.density = 1;
groundFixtureDef.friction = 1;
groundBody.CreateFixture(groundFixtureDef);
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(SWF_HALF_WIDTH/PIXELS_TO_METRE,4);
var body:b2Body = _world.CreateBody(bodyDef);
var dynamicBox:b2PolygonShape = new b2PolygonShape();
dynamicBox.SetAsBox(1,1);
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.shape = dynamicBox;
fixtureDef.density = 1;
fixtureDef.friction = 0.3;
body.CreateFixture(fixtureDef);
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.SetSprite(debugSprite);
debugDraw.SetDrawScale(PIXELS_TO_METRE);
debugDraw.SetLineThickness( 1.0);
debugDraw.SetAlpha(1);
debugDraw.SetFillAlpha(0.4);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
_world.SetDebugDraw(debugDraw);
addEventListener(Event.ENTER_FRAME, update);
}
public function update(e: Event):void
{
var timeStep:Number = 1 / 30;
var velocityIterations:int = 6;
var positionIterations:int = 2;
_world.Step(timeStep,velocityIterations,positionIterations);
_world.ClearForces();
_world.DrawDebugData();
}
}
}


Pingback: Box2D 2.1a Tutorial part 3. Custom shapes and textures | Allan Bishop's Developer Blog
Pingback: Box2D Flash 2.1 Hello World Falling Boxes | Lon (Alonzo) Hosford's Bitbox