The beauty of Box2DFlash, is you don't have to understand how
it works to use it. Still understanding it's mechanics will give you a much greater controle.
Now to get right into it! First of all, in
order to do anything with Box2DFlash you're going to have to include
most (if not all) of the files. If you have the 349KB folder "Box2D"
in your project folder, then your includes will work just like this:
import flash.display.Sprite;
import flash.events.Event;
// Classes used in this example
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
Easy.
Now you have to create a b2World object
(source code of the class is in Box2D/Dynamics/b2World.as). The
world object is the entire body of the engine. Everything is
contained within it once your are done.
NOTE: The brain of the engine
is b2BroadPhase.as and the heart is the Step() function withing the
world object. Don't go messing with the brain (b2BroadPhase.as)
ever. You will totally mess up the engine.
The world object constructor requires 3
things:
1.) A
coordinate system in the form of a b2AABB class object.
2.) A
vector that defines gravity. That will be in the form of a b2Vec2
class object.
3.) A
boolean variable that defines whether objects "sleep" or
not. (I recommend you make it true, that
they can sleep)
// Create world AABB
var worldAABB:b2AABB = new b2AABB();
worldAABB.minVertex.Set(-1000.0, -1000.0);
worldAABB.maxVertex.Set(1000.0, 1000.0);
// Define the gravity vector
var gravity:b2Vec2 = new b2Vec2(0.0, 300.0);
// Allow bodies to sleep
var doSleep:Boolean = true;
// Construct a world object
m_world = new b2World(worldAABB, gravity, doSleep);
My examples are not my own here. The
code I'm showing is an excerpt from the "Hello World" code
they provided with the engine.
After creating a world object you have
to bring it to life by setting its heart to beating:
// Add event for main loop
addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
public function Update(e:Event):void{
m_world.Step(m_timeStep, m_iterations);
}
Running the Step() function every frame
will update all the Body Definitions you add to your world.
As of right now this world is empty
though. In order to add balls and boxes and any strange polygonal
shapes you can think of, we need to create Body Definitions for them.
A Body Definition consists of 2, 3, or
4 things.
1.) A
Shape Definition.
2.) An
(x,y) position.
optional:
3.) Rotation (in
radians)
4.) A pre-made Sprite
object.
In the example flash movie on the Box2DFlash you will notice that all the shapes are
simple. That's because everything is being redrawn every frame with
only lines and no fill. That's right, EVERYTHING is made in code.
Nothing was drawn by hand.
If you want your game, or whatever, to
have a little more character then that example movie, then you will
probably want to associate hand made Sprites with your Body
Definitions.
In the "Hello World" example,
they use Sprites to display their objects. You could leave them
invisible too if you really wanted. Either way they will still be
accounted for in the calculations.
On to something very important. What
is a Shape Definition!? We have 3 types of shape definition and they
all extend the base b2ShapeDef class.
First we have the b2BoxDef class. The
b2BoxDef has 4 important properties:
1.) Extents
- this is a vector that essentially goes from one corner of the box
to the exact center. In other words, half the width and hight. (box
is a rectangle)
2.)
Density - in the collision equations we use density * area = mass
A density of 0 (zero) or null will make
the object static and it will never move in the case of a collision
or gravity.
3.) Friction
- this is used to calculate the friction between 2 objects... you
should keep it between 0.0 and 1.0
4.) Restitution
- this is the bounciness of the object. Should probably also stay
between 0.0 and 1.0
The b2CircleDef has only one
differences in it's properties. Instead of Extents it has Radius,
which is easy to remember.
The b2PolyDef has an array of (max 8)
vertices instead of Extents or Radius. These vertices are just
b2Vec2 vector objects.
Now Adding a bunch of objects to our
world should be easy:
var bodyDef:b2BodyDef;
var boxDef:b2BoxDef;
var circleDef:b2CircleDef;
// Add ground body
bodyDef = new b2BodyDef();
boxDef = new b2BoxDef();
boxDef.extents.Set(1000, 100);
boxDef.friction = 0.3;
/*Notice that the ground object has no density like the later
definitions. That's because it is static and we don't want it
effected by any forces.*/
bodyDef.position.Set(320, 400);
bodyDef.AddShape(boxDef);
// Add sprite to body userData
/*We have a Sprite object in the library called PhysGround. Here
we are associating that with our body definition.*/
bodyDef.userData = new PhysGround();
bodyDef.userData.width = boxDef.extents.x * 2;
bodyDef.userData.height = boxDef.extents.y * 2;
addChild(bodyDef.userData);
m_world.CreateBody(bodyDef);
// Add some objects
for (var i:int = 1; i < 20; i++){
/*We are going to re-use the same bodyDef from before.
It doesn't matter now, because it's already been copied and stored
in our world object.*/
bodyDef = new b2BodyDef();
// Box
if (Math.random() < 0.5){
boxDef = new b2BoxDef();
boxDef.extents.Set(Math.random() * 15 + 10, Math.random() * 15 + 10);
boxDef.density = 1.0;
boxDef.friction = 0.5;
boxDef.restitution = 0.2;
bodyDef.AddShape(boxDef);
/*We have a Sprite object in the library called PhysBox.*/
bodyDef.userData = new PhysBox();
bodyDef.userData.width = boxDef.extents.x * 2;
bodyDef.userData.height = boxDef.extents.y * 2;
}
// Circle
else {
circleDef = new b2CircleDef();
circleDef.radius = Math.random() * 15 + 10;
circleDef.density = 1.0;
circleDef.friction = 0.5;
circleDef.restitution = 0.2
bodyDef.AddShape(circleDef);
/*We have a Sprite object in the library called PhysCircle.*/
bodyDef.userData = new PhysCircle();
bodyDef.userData.width = circleDef.radius * 2;
bodyDef.userData.height = circleDef.radius * 2;
}
bodyDef.position.x = Math.random() * 400 + 120;
bodyDef.position.y = Math.random() * 100 + 50;
m_world.CreateBody(bodyDef);
addChild(bodyDef.userData);
}
That's great isn't it? It's really not
that hard to add a whole bunch of objects to your world. You should
know that the Step() function that we added earlier will only take
care of moving and rotating our body definitions. If we have sprites
to represent those definitions then we need to update them
manually... they made the code for that easy and you can copy and
paste it almost exactly into every one of your projects.
We just have to rewrite our Update
function from before:
public function Update(e:Event):void{
m_world.Step(m_timeStep, m_iterations);
// Go through body list and update sprite positions/rotations
for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next){
if (bb.m_userData is Sprite){
bb.m_userData.x = bb.m_position.x;
bb.m_userData.y = bb.m_position.y;
bb.m_userData.rotation = bb.m_rotation * (180/Math.PI);
}
}/*This is an extreemly clever way to do this, and I suggest you copy it exactly in your own projects.*/
}
That's it for now. We have objects in
a world that can collide with each other and are affected by gravity.
There is still a lot more to talk about, but I am just walking you
through the "Hello World" first. We still need to cover
Joints, Pulleys, Gears, Compound Shapes, Forced rotation (like cars
tires), and a lot more... I doubt I will be able to cover all these
topics anytime soon, but I will definitely write more tutorials on
this physics engine in the future.
You can download my copy of the "Hello
World" example project with all of my comments included (there
is almost no commenting in the original).
Download
Hello World w/
comments
You can get all of the library files
needed for Box2DFlash
at their site. It's free.
Credits:
Box2D - Erin Catto (http://gphysics.com/
)
Box2DFlashAS3 - Matthew Bush (Flash
ActionScript 3.0 port of Erin Catto's work)
At the time of this article I am writting about Box2DFlashAS3 v1.4.3
As I understand it they are currently working on Porting v2 of Box2D to flash and I'm not sure what changes there will be.