If you want to create 2D physics-driven games and applications, Box2D is the best choice available. Box2D is a 2D rigid body simulation library used in some of the most successful games, such as Angry Birds and Tiny Wings on iPhone or Totem Destroyer and Red Remover on Flash. Google them, and you'll see a lot of enthusiastic reviews.
Before we dive into the Box2D World, let me explain what is a rigid body . It's a piece of matter that is so strong that it can't be bent in any way. There is no way to modify its shape, no matter how hard you hit it or throw it. In the real world, you can think about something as hard as a diamond, or even more. Matter coming from outer space that can't be deformed.
Box2D only manages rigid bodies, which will be called just "bodies" from now on, but don't worry, you will also be able to simulate stuff which normally is not rigid, such as bouncing balls.
Let's see what you are about to learn in this chapter:
Downloading and installing Box2D for Flash
Including required classes in your Flash projects
Creating your first Box2D World
Understanding gravity and sleeping bodies
Running your first empty simulation, handling time steps, and constraints
By the end of the chapter, you will be able to create an empty, yet running world where you can build your awesome physics games.
You can download the latest version of Box2D for Flash either from the official site (http://www.box2dflash.org/download) or from the SourceForge project page (http://sourceforge.net/projects/box2dflash/).
Once you have downloaded the zipped package, extract the Box2D
folder (you can find it inside the Source
folder) into the same folder you are using for your project. The following is how your awesome game folder should look before you start coding:
You can see the Box2D
folder, the FLA file that I am assuming has a document class called Main
and therefore Main.as
, which is the class we will work on.
I would suggest you work on a 640 x 480 Flash movie at 30 frames per second (fps). The document class should be called Main
and the examples will look better if you use a dark stage background color, such as #333333
. At least these are the settings I am using throughout the book. Obviously you can change them as you want, but in that case your final movies may look a bit different than the examples shown in the book.
Now let's import Box2D classes.
Box2D is free and open source, so you won't need to install components or deal with SWC files. All you need to do to include it in your projects is to include the required classes.
Open Main.as
and write the following code snippet:
package { import flash.display.Sprite import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class Main extends Sprite { public function Main() { trace("my awesome game starts here"); } } }
Test the movie and you should see my awesome game starts here in your Output window. This means you have successfully imported the required classes.
There isn't that much to say about the code we just wrote, as we are just importing the classes needed to make our Box2D project work.
When I gave the Hello Box2D World title, I did not mean to create just another "Hello World" section, but I wanted to introduce the environment where all Box2D simulation and events take place: the world.
The world is the stage where the simulation happens. Everything you want to be ruled by the Box2D physics must be inside the world. Luckily, the Box2D World is always big enough to contain everything you need, so you don't have to worry about world boundaries. Just remember everything on a computer has limits in one way or another. So, the bigger the world, the heavier will be the work for your computer to manage it.
Like all worlds, the Box2D World has a gravity , so the first thing you need to do is define world gravity.
In your
Main
function, add the following line:var gravity:b2Vec2=new b2Vec2(0,9.81);
This introduces our first Box2D data type:
b2Vec2
.b2Vec2
is a 2D column vector that is a data type, which will store x and y components of a vector. As you can see, the constructor has two arguments, both numbers, representing the x and y components. This way we are defining thegravity
variable as a vector withx=0
(which means no horizontal gravity) andy=-9.81
(which approximates Earth gravity).Physics says the speed of an object falling freely near the Earth's surface increases by about 9.81 meters per second squared, which might be thought of as "meters per second, per second". So assuming there isn't any air resistance, we are about to simulate a real-world environment. Explaining the whole theory of a falling body is beyond the scope of this book, but you can get more information by searching for "equations for a falling body" on Google or Wikipedia.
You can set your game on the move with the following line:
var gravity:b2Vec2=new b2Vec2(0,1.63);
You can also simulate a no gravity environment with the arguments set at
(0,0)
:var gravity:b2Vec2=new b2Vec2(0,0);
We also need to tell if bodies inside the world are allowed to sleep when they come to rest, that is when they aren't affected by forces. A sleeping body does not require simulation, it just rests in its position as its presence does not affect anything in the world, allowing Box2D to ignore it, and thus speeding up the processing time and letting us achieve a better performance. So I always recommend to put bodies to sleep when possible.
Add the following line, which is just a simple Boolean variable definition:
var sleep:Boolean=true;
And finally, we are ready to create our first world:
var world:b2World = new b2World(gravity,sleep);
Now we have a container to manage all the bodies and perform our dynamic simulation.
Time to make a small recap. At the moment, your code should look like the following:
package { import flash.display.Sprite; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class Main extends Sprite { public function Main() { var gravity:b2Vec2=new b2Vec2(0,9.81); var sleep:Boolean=true; var world:b2World = new b2World(gravity,sleep); } } }
Now you learned how to create and configure a Box2D World. Let's see how can you simulate physics in it.
You need to run the simulation at every frame, so first of all you need a listener to be triggered at every frame.
Let's make some simple changes to our class:
package { import flash.display.Sprite; import flash.events.Event; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class Main extends Sprite { public function Main() { var gravity:b2Vec2=new b2Vec2(0,9.81); var sleep:Boolean=true; var world:b2World = new b2World(gravity,sleep); addEventListener(Event.ENTER_FRAME,updateWorld); } private function updateWorld(e:Event):void { trace("my awesome simulation runs here"); } } }
Nothing new, we just added an
ENTER_FRAME
event, but we needed it in order to run the simulation inside theupdateWorld
function. If you have doubts regarding event handling with AS3, refer to the official Adobe docs or get Flash Game Development by Example, Packt Publishing, which will guide you to a step-by-step creation of pure AS3 games.Box2D simulation works by simulating the world at discrete steps of time. This means the world gets updated at every time step. It's up to us to decide which time step we are going to use for our simulation. Normally, physics in games have a time step of 1/60 seconds. Anyway, as I am running the Flash movie at 30 fps, I am going to set a time step of 1/30 seconds.
The first line into the
updateWorld
function will be:var timeStep:Number=1/30
Just defining a time step, is not enough. At every step, every physic entity is updated according to the forces acting on it (unless it's sleeping). The algorithm which handles this task is called constraint solver. It basically loops through each constraint and solves it, one at a time. If you want to learn more about constraints, search for "constraint algorithm" on Google or Wikipedia.
Where's the catch? While a single constraint is solved perfectly, it can mess with other constraints that have already been solved.
Think about two balls moving: in the real world, each ball position is updated at the same time. In a computer simulation, we need to loop through the balls and update their position one at a time. Think about a
for
loop that updates a ball at every iteration. Everything works as long as the balls do not interact with each other, but what happens if the second ball hits the first, whose position has already been updated? They would overlap, which is not possible in a rigid body simulation.To solve this problem with a reasonable approximation, we need to loop over all the constraints more than once. Now the question is: how many times?
There are two constraint solvers: velocity constraint solver and position constraint solver . The velocity solver is used to move physic entities in the world according to their impulses. The position solver adjusts physic entities' positions to avoid overlap.
So it's obvious that the more iterations there are, the more accurate the simulation, and the lower will be the performance. I managed to handle more than 100 physic entities using 10 velocity and position iterations, although the author of Box2D recommends 8 for velocity and 3 for position.
It's up to you to play with these values. Meanwhile, I'll be using
10
iterations for both constraint solvers.So here we go with two new variables:
var velIterations:int=10; var posIterations:int=10;
Finally we are ready to call the
Step
method on theworld
variable to update the simulation.To use
world
inside theupdateWorld
function, we need to declareworld
as a class-level variable, shown as follows:package { import flash.display.Sprite; import flash.events.Event; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class Main extends Sprite { private var world:b2World; public function Main() { var gravity:b2Vec2=new b2Vec2(0,9.81); var sleep:Boolean=true; world=new b2World(gravity,sleep); addEventListener(Event.ENTER_FRAME,updateWorld); } private function updateWorld(e:Event):void { var timeStep:Number=1/30; var velIterations:int=10; var posIterations:int=10; world.Step(timeStep,velIterations,posIterations); } } }
Now we have our world configured and running. Unfortunately, it's a very boring world, with nothing in it. So in the next chapter, we are going to populate the world with all kinds of physic entities.
Just one last thing, after each step you need to clear forces, to let the simulation start again at the next step.
You can do it by adding the following line right after the
Step
method:world.ClearForces();Your final code is shown as follows:
package { import flash.display.Sprite; import flash.events.Event; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class Main extends Sprite { private var world:b2World; public function Main() { var gravity:b2Vec2=new b2Vec2(0,9.81); var sleep:Boolean=true; world=new b2World(gravity,sleep); addEventListener(Event.ENTER_FRAME,updateWorld); } private function updateWorld(e:Event):void { var timeStep:Number=1/30; var velIterations:int=10; var posIterations:int=10; world.Step(timeStep,velIterations,posIterations); world.ClearForces(); } } }
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
And now you are really ready to place some action in your Box2D World.
You have just learned how to install Box2D for Flash. Include it in your projects and create a running, gravity-ruled simulation managing time steps and constraint solvers.
You have an empty world ready to be the container where your awesome game will take place. Save it and use it in every future project!