Box2D basics - Part 1

February 12th, 2012    by sigman    8630
  Primers   box2d, actionscript, programming

Box2D logoBox2D probably doesn't have to be introduced to anyone, even if you have never used it, you have probably still heard about it. This is the most popular physics simulation engine that has been ported to every popular platform available. I had a chance to work with Box2D in C++ and I have been reading tutorials from guys using it with Python, Objective-C, Java and JavaScript. So if you come from other platform than Flash, I encourage you to read this article anyway as although I'm using a Flash port here, everything works exactly the same way in other platforms. In the first part, I cover how to create a world, static and dynamic objects, how to add interaction and work with collision detection.

Article imported from http://sierakowski.eu/list-of-tips/114-box2d-basics-part-1.html

There are two latest major versions of Box2D, 2.0 and 2.1 and there are not compatible. It means code written in 2.0 will not work in 2.1. There aren't big changes but just giving you a heads up that when following 2.0 tutorials but working with 2.1 libraries plugged in to your IDE you may experience problems.

When you start working with Box2D you will quickly realise that all data types are prefixed with b2. This is to eliminate a risk of having conflicts in naming between the framework and your project.

 

Units

Before start coding, there is one important value that we need to set:

private const pixelsPerMeter:Number = 30;

 

Quoting the manual: "Box2D works with floating point numbers, so some tolerances have to be used to make Box2D perform well. These tolerance have been tuned to work well with meters-kilogram-second (MKS) units. In particular, Box2D has been tuned to work well with moving objects between 0.1 and 10 meters. So this means objects between soup cans and buses in size should work well."

We don't want one pixel represent one meter as considering our stage size, some objects that are 1000 pixels long would have one kilometer in Box2D world. The standard is that one pixel will be represented by 1/30 meter. This means that when working with objects, specifying their sizes or positions, we will need to convert pixels to meters. To make it easier let's create a helper function that will do a conversion for us:

private function p2m(pixels:Number):Number
{
  return pixels / pixelsPerMeter;
}

 

Debug Draw

We are going to use the debug drawing to see the graphical representation of what is happening in our world. This isn't a necessary step, but remember that Box2D operates on numbers and returns numbers.

As the name implies this is only for debugging and you won't be using this in your games. Later on we will look at using our own graphical assets, we will be reading physical objects positions and move our sprites or bitmaps accordingly.

Debug draw can show the following things:

  • shape outlines
  • joint connectivity
  • core shapes (for continuous collision)
  • broad-phase axis-aligned bounding boxes (AABBs), including the world AABB
  • polygon oriented bounding boxes (OBBs)
  • broad-phase pairs (potential contacts)
  • center of mass

You can customise your view using drawFlags that are defined in the b2DebugDraw class.

Let's declare our debugDraw globally and create createDebugDraw function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private var debugDraw:b2DebugDraw;
 
private function initDebugDraw():void
{
  debugDraw = new b2DebugDraw();
  var debugSprite:Sprite = new Sprite();
  addChild(debugSprite);
  debugDraw.SetSprite(debugSprite);
  debugDraw.SetDrawScale(pixelsPerMeter);
  debugDraw.SetFillAlpha(.3);
  debugDraw.SetLineThickness(1.0);
  debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
  ourWorld.SetDebugDraw(debugDraw);
}

 

Then just in few moments, when we create a world object and the update function called on every step, we also call the DrawDebudData method to update the view and reflect changes in the physics world:

ourWorld.DrawDebugData();

 

And that would be it about the debug view. However, for the purpose of this tutorial, I wanted to make it easier to change the debug view at runtime and read information about the main object - our hero's position, speed and collisions. I used components (minimalcomps from bit101) and created buttons representing each type of debug view. Have a look at the final code for this tutorial to see the implementation.

 

World creation

World object of type b2World is the physics hub that manages memory, objects, and simulation and it's the first thing that we create.

private var ourWorld:b2World;

 

To keep everything clean let's create the "createWorld" function that will set up our world. Constructor of b2World takes two parameters, a gravity vector and a boolean indicating whether bodies will be allowed to sleep. In our case the gravity will be on y axis down (as in our real world) and bodies are allowed to sleep (if there are no collisions bodies will be temporary disabled to save CPU some work).

private var createWorld():void
{
  var gravity:b2Vec2 = new b2Vec2(0.0, -10.0);
  var doSleep:Boolean = true;
 
  ourWorld = new b2World(gravity, doSleep);
}

 

Conducting simulation

Let's create two more constants. The first is a time-step which is number of steps in a second is needed for the Integrator, the part of Box2D which simulates physics equations at discrete points of time. This value shouldn't be changed.

private const timeStep:Number = 1.0 / 60.0;

 

The second is a number of iterations for the Constraint Solver which is responsible for solving constraints in the simulation. We surely need more than one at a time step as "when we solve one constraint, we slightly disrupt other constraints. The iteration count controls how many times the constraint solver sweeps over all the contacts and joints in the world. More iterations always yields a better simulation. But don't trade a small time step for a large iteration count. 60Hz and 10 iterations is far better than 30Hz and 20 iterations."

private const iterations:Number = 10;

 

In order to get the simulation working we need to plug it to a loop. We should avoid Box2D updates depending on a frame rate (that may be changing depending on the CPU load) but in Flash because of the way how the code execution is tied in to frames generation, we just use a standard enterFrame event. Let's create the update function that will be plugged to ENTER_FRAME listener.

private function updatePhysics(e:Event = null):void
{
  ourWorld.Step(timeStep, iterations, iterations);
  ourWorld.ClearForces();
 
  ourWorld.DrawDebugData();
}

 

To perform a step we need to pass our time-step value and iterations values for velocity and position iterations. After each step we clearForces and also update our debug view.

 

Creating objects

This might look a bit complicated at first but it makes sense when you get some practise and better understanding of Box2D.

There are three major sub-objects that create a complete physical object: shape, fixture and body.

  • Shape defines a geometric shape of an object such as radius for circle, vertices for polygons. It is used to calculate a mass of an object and for collision detection.
  • Fixture defines material properties of an object such as density and friction. It also allows to add a userData - a pointer to an object containing data defined by yourself.
  • Body uses fixtures and shapes and defines position in the world and object's velocity.

 

Bodies Fixtures Shapes explanation

 


Creating static objects

There are three types of bodies: static, dynamic and kinetic (the last one introduced in v2.1). The main feature of bodies is that you can apply forces, torques, and impulses to them. Static bodies have mass equal to zero, never move and don't collide with other static bodies. Bodies are always rigid bodies which meant that two shapes attached to the same rigid body never move relative to each other.

We will create four static bodies that will be walls in our world. Let's create the createWorldWalls function for that purpose. In order to create a body we need to create a body definition first that will be used to specify mass and position. The default mass is zero and since we are creating static bodies this is what we need. Let our wall have thickness of 50 pixels and width and height matching our game width and height size. Note that when creating shapes, the constructors take half of the width and half of the height. When positioning bodies however, Box2D sets position of the center of an object, not the top left like in Flash, so we have to add the middle (width/2, height/2) to the position to get it where we actually want to have it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private static const GAME_AREA_WIDTH  :Number = 500;
private static const GAME_AREA_HEIGHT :Number = 400;
 
private function createWorldWalls():void
{
  var wallThickness:Number = 10;
  var wallWidth:Number = 490;
  var wallHeight:Number = 390;
 
  // Definitions can be reused
  var wallShape:b2PolygonShape = new b2PolygonShape();
  var wallBodyDef:b2BodyDef = new b2BodyDef();
  var wallBody:b2Body;
 
  // Left / right wall shape
  wallShape.SetAsBox(p2m(wallThickness / 2), p2m(wallHeight / 2));
 
  // left
  wallBodyDef.position.Set(p2m(wallThickness), p2m(GAME_AREA_HEIGHT / 2));
  wallBody = ourWorld.CreateBody(wallBodyDef);
  wallBody.CreateFixture2(wallShape);
 
  // right
  wallBodyDef.position.Set(p2m(GAME_AREA_WIDTH - wallThickness), 
                                               p2m(GAME_AREA_HEIGHT / 2));
  wallBody = ourWorld.CreateBody(wallBodyDef);
  wallBody.CreateFixture2(wallShape);
 
  // Top / bottom wall shape
  wallShape.SetAsBox(p2m(wallWidth / 2), p2m(wallThickness / 2));
 
  // top 
  wallBodyDef.position.Set(p2m(GAME_AREA_WIDTH / 2), p2m(wallThickness));
  wallBody = ourWorld.CreateBody(wallBodyDef);
  wallBody.CreateFixture2(wallShape);
 
  // bottom
  wallBodyDef.position.Set(p2m(GAME_AREA_WIDTH / 2), 
                                    p2m(GAME_AREA_HEIGHT - wallThickness));
  wallBody = ourWorld.CreateBody(wallBodyDef);
  wallBody.CreateFixture2(wallShape);
}

 

Do you see a pattern here? To create a body you need a body definition. You create a shape and a fixture and plug it in to a body. And to create body you use the world object.

Important note: Do not instantiate bodies and joints with the 'new' operator though. By using the world object, you let Box2d manage them and utilise memory when they are destroyed.

So this is how our world looks like. Just four walls around the stage.

 

Creating dynamic objects

Now once we have four walls surrounding our world, let's create some dynamic bodies, let it be some boxes that will be dropped from the middle of our stage. The make it easier let's create function 'createBox' that will take x, y, width, height. In addition let's add few more properties like isDynamic so we could use this function to create static bodies as well, density, friction, fixedRotation and name string. Function will return a body object that we may keep for a reference if required.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private function createBox(x:Number, y:Number, 
                           width:Number, height:Number, 
                           isDynamic:Boolean, 
                           density:Number = 1, friction:Number = .5, 
                           fixedRotation:Boolean = false,
                           name:String = ""):b2Body
{
  var bWidth:Number = p2m(width);
  var bHeight:Number = p2m(height);
 
  // box shape
  var shape:b2PolygonShape = new b2PolygonShape();
  shape.SetAsBox(bWidth / 2, bHeight / 2);
 
  // fixture
  var fixture:b2FixtureDef = new b2FixtureDef();
  fixture.density = density;
  fixture.friction = friction;
  fixture.shape = shape;
  fixture.userData = new UserDataInfo(name, bWidth, bHeight);
 
  // body definition
  var bodyDef:b2BodyDef = new b2BodyDef();
  bodyDef.position.Set(p2m(x) + bWidth / 2, p2m(y) + bHeight / 2);
  bodyDef.type = isDynamic ? b2Body.b2_dynamicBody : b2Body.b2_staticBody;
  bodyDef.fixedRotation = fixedRotation;
 
  // body
  var body:b2Body = ourWorld.CreateBody(bodyDef);
  body.CreateFixture(fixture);
  return body;
}

 

First we convert width and height from pixels to Box2D units - meters.

Next we create a shape. As this is going to be a box, as when we created our walls, we use the b2PolygonShape class again. Polygons could be created with one of the following methods available in the definition of the class:

  • SetAsBox - takes just half width and height of a box and positions it in the center
  • SetAsOrientedBox - allows also setting position of a box
  • SetAsArray - takes a list of vertices to create more complex polygons

It is always a good idea to go through the Box2D classes and review their methods as documentation may not be up to date, especially when there is a new release.

The next part of code is actually the only new thing here - the fixture definition. When creating the static walls we used a shortcut and fixtures were created automatically for us. Now we want to be more specific and we aren't going to use defaults, so we create one and attach our shape to it among other details like density, friction and userData object.

Just to say a little more about friction properties:

  • Friction makes objects slide along each other realistically. The parameter is usually between 0 and 1. A value of zero turns off friction and a value of one makes the friction strong. The friction is computed between two shapes with the following formula: Math.sqrt(shape1.friction * shape2.friction).
  • Restitution makes objects bounce and its value is usually set between 0 and 1. A value of zero means an object won't bounce. This is called an inelastic collision. A value of one means that an object's velocity will be exactly reflected. This is called a perfectly elastic collision. Restitution is combined using the following formula: Math.Max(shape1.restitution, shape2.restitution).
  • UserData allows to point to a custom object. You don't have to use it if you don't need to track your object down.
  • Shape is used to connect a shape object.

Next we create a body definition and specify its position and type: dynamic or static. We can also specify if it's going to rotate or not. Once definition is created, we use the world object to create a new body and using that body we can plug a fixture from our fixture definition.

For the userData part of the fixture we could use just a string, number or we could create a custom object. To keep consistency, we will create a class UserDataInfo that will hold width, height and object's name. We will use these information when working on collisions later on.

1
2
3
4
5
6
7
8
9
10
11
12
13
class UserDataInfo
{
  public var name:String;
  public var width:Number, height:Number;
 
  public function UserDataInfo(name:String = "", 
                               width:Number = 0, height:Number = 0):void
  {
    this.name = name;
    this.width = width;
    this.height = height;
  }
}

 

Now when we have our box creation function in place, let's create one static body - a platform in the middle of a stage and few dynamic boxes with random size and position.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private function createWorldObjects():void
{
  var i:int;
 
  // Create platfroms - static boxes
  createBox(10, GAME_AREA_HEIGHT / 2, GAME_AREA_WIDTH / 3, 
                            10, false, 1, .5, false, "platform");
 
  // Create some random dynamic boxes
  var offsetX:Number = 40;
  var offsetTopY:Number = 20;
  var offsetBottomY:Number = GAME_AREA_HEIGHT / 2;
  var maxWidthHeight:Number = 20;
  var rX:Number, rY:Number, rWidth:Number, rHeight:Number;
  var fixedRotation:Boolean;
 
  for (i = 0; i < 10; i++)
  {
    rX = Math.random() * (GAME_AREA_WIDTH - 2 * offsetX) + offsetX;
    rY = Math.random() * 
            (GAME_AREA_HEIGHT - offsetBottomY - offsetTopY) + offsetTopY;
    rWidth = Math.random() * maxWidthHeight + 10;
    rHeight = Math.random() * maxWidthHeight + 10;
    fixedRotation = Math.random() >= .5;
 
    createBox(rX, rY, rWidth, rHeight, true, 1, .5, 
                                               fixedRotation, "box" + i);
  }
}

 

We aren't limited just to boxes, we can provide a list of vertices ourselves and create other type of polygons - triangles. Let's create some triangles with a function very similar to the createBox. There is one important thing to note quoting the Box2D manual: "When you build a polygon definition you must specify the number of vertices you will use. The vertices must be specified in counter-clockwise (CCW) order about the z-axis of a right-handed coordinate system. This might turn out to be clockwise on your screen, depending on your coordinate system conventions". In practise if you are not sure what's your coordinate system, you can always try both ways, an indication that the order is incorrect would be that your polygons will not properly collide with other objects (fall through them). The same thing may happen if a polygon is not convex, each vertex must point outwards to some degree. Vertices can't overlap as well. For our triangles below the vertices are provided in clockwise order.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private function createTriangle(x:Number, y:Number, 
                                width:Number, height:Number, 
                                isDynamic:Boolean, 
                                density:Number = 1, friction:Number = .5, 
                                fixedRotation:Boolean = false, 
                                name:String = ""):b2Body
{
  var bWidth:Number = p2m(width);
  var bHeight:Number = p2m(height);
 
  // shape
  var shape:b2PolygonShape = new b2PolygonShape();
  var vertices:Array = [];
  vertices.push(new b2Vec2(bWidth / 2, bHeight / 2));  // right bottom
  vertices.push(new b2Vec2(-bWidth / 2, bHeight / 2)); // left bottom
  vertices.push(new b2Vec2(0, -bHeight / 2));          // middle top
  shape.SetAsArray(vertices);
 
  // fixture
  var fixture:b2FixtureDef = new b2FixtureDef();
  fixture.density = density;
  fixture.friction = friction;
  fixture.shape = shape;
  fixture.userData = new UserDataInfo(name, bWidth, bHeight);
 
  // body definition
  var bodyDef:b2BodyDef = new b2BodyDef();
  bodyDef.position.Set(p2m(x) + bWidth / 2, p2m(y) + bHeight / 2);
  bodyDef.type = isDynamic ? 
                          b2Body.b2_dynamicBody : b2Body.b2_staticBody;
  bodyDef.fixedRotation = fixedRotation;
 
  // body
  var body:b2Body = ourWorld.CreateBody(bodyDef);
  body.CreateFixture(fixture);
  return body;
}

 

And let's plug triangles to our makeWorldObjects function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create some random triangles
for (i = 0; i < 10; i++)
{
  rX = Math.random() * 
                (GAME_AREA_WIDTH - 2 * offsetX) + offsetX;
  rY = Math.random() * 
                (GAME_AREA_HEIGHT - offsetBottomY - offsetTopY) + offsetTopY;
  rWidth = Math.random() * maxWidthHeight + 10;
  rHeight = Math.random() * maxWidthHeight + 10;
  fixedRotation = Math.random() >= .5;
 
  createTriangle(rX, rY, rWidth, rHeight, true, 1, .5, 
                                              fixedRotation, "triangle" + i);
}

 

We have boxes and triangles so let's add some circles as well. We will create another function createCircle, very similar to createBox and createTriangle but instead of width and height we will pass radius and to create a circular shape we will use b2CircleShape object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private function createCircle(x:Number, y:Number, 
                              radius:Number, 
                              isDynamic:Boolean, 
                              density:Number = 1, friction:Number = .5, 
                              fixedRotation:Boolean = false, 
                              name:String = ""):b2Body
{
  var bRadius:Number = p2m(radius);
 
  // circle shape
  var shape:b2CircleShape = new b2CircleShape(bRadius);
 
  // fixture
  var fixture:b2FixtureDef = new b2FixtureDef();
  fixture.density = density;
  fixture.friction = friction;
  fixture.shape = shape;
  fixture.userData = new UserDataInfo(name, bRadius * 2, bRadius * 2);
 
  // body definition
  var bodyDef:b2BodyDef = new b2BodyDef();
  bodyDef.position.Set(p2m(x) + bRadius / 2, p2m(y) + bRadius / 2);
  bodyDef.type = isDynamic ? 
                         b2Body.b2_dynamicBody : b2Body.b2_staticBody;
  bodyDef.fixedRotation = fixedRotation;
 
  // body
  var body:b2Body = ourWorld.CreateBody(bodyDef);
  body.CreateFixture(fixture);
  return body;
}

 

And let's add this code to the makeWorldObjects function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create some random circles
var rRadius:Number;
var maxRadius:Number = 10;
 
for (i = 0; i < 10; i++)
{
  rX = Math.random() * (GAME_AREA_WIDTH - 2 * offsetX) + offsetX;
  rY = Math.random() * 
            (GAME_AREA_HEIGHT - offsetBottomY - offsetTopY) + offsetTopY;
  rRadius = Math.random() * maxRadius + 10;
  fixedRotation = false;
 
  createCircle(rX, rY, rRadius, true, 1, .5, fixedRotation, "circle" + i);
}

 

Just in a moment we will start applying forces and impulses, but before let's create one more a little bit more complex object, made of different shapes. You can add multiple shapes to one body, remember that bodies are rigid so you nothing can breakthem. Additional shapes are not for visual effect, they will form more complex object for collision detection and may change the center of a mass in a body. I we add a very long left hand to a human object, it will move a mass to that side and if that object can rotate, it will probably fall on that side.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private function createHero(x:Number, y:Number):b2Body
{
  // fixture
  var fixture:b2FixtureDef = new b2FixtureDef();
  fixture.friction = .5;
  fixture.userData = new UserDataInfo("hero", p2m(30), p2m(60));
 
  // body definition
  var bodyDef:b2BodyDef = new b2BodyDef();
  bodyDef.position.Set(p2m(x + 15 / 2), p2m(y + 30 / 2));
  bodyDef.type = b2Body.b2_dynamicBody;
  bodyDef.fixedRotation = true;
 
  // body
  var body:b2Body = ourWorld.CreateBody(bodyDef);
 
  // shapes
  var headShape:b2CircleShape = new b2CircleShape(p2m(10));
  headShape.b2internal::m_p.Set(0, p2m(-20));
  fixture.shape = headShape;
  body.CreateFixture(fixture);
 
  var boxShape:b2PolygonShape = new b2PolygonShape();
  boxShape.SetAsBox(p2m(15), p2m(10));
  fixture.shape = boxShape;
  body.CreateFixture(fixture);
 
  var leftLegShape:b2PolygonShape = new b2PolygonShape();
  leftLegShape.SetAsArray(
    [
      new b2Vec2(p2m(30 / 2), p2m(20 / 2)),  // right top
      new b2Vec2(p2m(20 / 2), p2m(60 / 2)),  // right bottom
      new b2Vec2(p2m(-20 / 2), p2m(60 / 2)), // left bottom
      new b2Vec2(p2m(-30 / 2), p2m(20 / 2))  // left top
    ]);
  fixture.shape = leftLegShape;
  body.CreateFixture(fixture);
 
  return body;
}

 

Now let's create global variable hero and create hero in createWorldObjects function:

hero = createHero(40, 20);

 

To tell the truth, most of the simple games based on Box2D have hero which is just a simple box shape and a bitmap representing it has a shape of human, robot or whatever the hero might be. But there are also games that have really very complex structures like cross country cars or even tanks made of many objects.

This is the current state of our project. Notice how objects change their colors to gray just after few seconds from the start of the simulation, this indicates that they are sleeping. Do you remember the doSleep parameter when creating the world object - we allowed bodies to sleep? This means that every time a body becomes inactive - it's velocity is zero and is not colliding with other body that has some velocity, it will be removed from the simulation what will improve the performance. But if a body is in the sleep mode it doesn't mean it can't be woken up. You can check if body is sleeping with IsSleeping and you can wake it up with WakeUp, both are methods of the body object. Body will also be woken up if there is a collision or a force applied to it.


Using forces to control objects

You can apply forces, torques, and impulses to a body. When applying a force or an impulse, you need to provide a world point coordination where the load is going to be applied. Let's test this on our hero and add some way of controlling him using keyboard.

First we need to add a way to find out what keys are pressed by a user. We will create a global array keysDown that will hold all keys that are currently down (pressed) and then add necessary listeners to our init function. We will also create listener handlers that will mark what keys are currently down and a little helper function returning boolean to check what key is currently pressed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private var keysDown:Array;
 
private function init():void
{
  if (hasEventListener(Event.ADDED_TO_STAGE)) 
    removeEventListener(Event.ADDED_TO_STAGE, init);
 
  initDebugUI();
  createWorld();
  initDebugDraw();
  createWorldWalls();
  createWorldObjects();
 
  addEventListener(Event.ENTER_FRAME, updatePhysics);
 
  keysDown = [];
  stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
  stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
 
protected function onKeyDown(e:KeyboardEvent):void
{
  keysDown[e.keyCode] = true;
}
 
protected function onKeyUp(e:KeyboardEvent):void
{
  keysDown[e.keyCode] = false;
}
 
public function isKeyDown(keyCode:int):Boolean
{
  return keysDown[keyCode];
}

 

Next let's two constants limiting hero's speed on x and y axis.

private const heroSpeedX:Number  = 2;
private const heroSpeedY:Number  = 50;

 

At the beginning of our updatePhysics function add these lines:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (isKeyDown(Keyboard.LEFT) && hero.GetLinearVelocity().x > -heroSpeedX)
{
  hero.ApplyImpulse(new b2Vec2(-heroSpeedX / 2, 0), hero.GetWorldCenter());
}
 
if (isKeyDown(Keyboard.RIGHT) && hero.GetLinearVelocity().x < heroSpeedX)
{
  hero.ApplyImpulse(new b2Vec2(heroSpeedX / 2, 0), hero.GetWorldCenter());
}
 
if (isKeyDown(Keyboard.UP))
{
  hero.ApplyForce(new b2Vec2(0, -heroSpeedY / 2), hero.GetWorldCenter());
}

 

What we are doing is we are checking if LEFT, RIGHT or UP button was pressed and in case of left and right we are also checking if the current hero's linear velocity is not greater than our limit in heroSpeedX as we don't want him to speed up out of control. If these conditions are met we apply impulse with power equal to hero's max speed to the center of our hero object using GetWorldCenter method. If we applied force to some other point than the center, the hero object would rotate (actually it wouldn't in our case as the body definition had fixedRotation parameter set to true).

 

There are three popular ways of having an interaction with objects in Box2D, all of them are methods of a body object (as they are applied to a body object):

  • applying impulse with ApplyImpulse
  • applying force with ApplyForce
  • setting linear velocity with SetLinearVelocity

 

Box2D documentation doesn't say much about differences but just by playing with it you can see the difference.

Try to change the heroSpeedY and the type of force applied:

// inside update:
if (isKeyDown(Keyboard.UP))
{
  // for heroSpeedY = 1
  //hero.ApplyImpulse(new b2Vec2(0, -heroSpeedY / 2), hero.GetWorldCenter());
 
  // for heroSpeedY = 50
  hero.ApplyForce(new b2Vec2(0, -heroSpeedY / 2), hero.GetWorldCenter());
 
  // for heroSpeedY = 10
  //hero.SetLinearVelocity(new b2Vec2(hero.GetLinearVelocity().x, -heroSpeedY / 2));
}

 

What you should notice is that the longer you keep the UP key down the more acceleration the hero gets, it means that the impulses or forces are added on top of the current speed. But applying the linear velocity doesn't do that, it only sets that speed and the hero never goes above that. Also notice that we apply different values to force and impulse to get the same hero's actual speed.

But what if we wanted to make our hero jumping instead of flying, like in platformer games?

 

Collisions - using contacts

We will start with a little bit of theory first. The most important thing to remember is that Box2D uses shapes (or rather fixtures containing shapes) for collision detection. Information about collisions is available in contacts which are objects of type b2Contact. We can get them either from a world object or specific body. In our case it makes sense to check collisions for the hero object. Contact objects contain pointers to two fixtures that are colliding with each other as well as other information. Contact objects are immediately created by Box2D when bounding boxes of two objects are colliding (this is called AABB - axis-aligned bounding box collision). Of course for circular or more complex polygonal object we want more precise detection than that, but no worries, contacts have IsTouching method that allows to check if the collision is actually happening.

We want our hero to jump only when he touches (stands on) a ground or other objects. Let's create a new global boolean variable canHeroJump and a function updateHeroCollision that will set canHeroJump to true or false depending on whether the hero touches anything.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private function updateHeroCollisions():void
{
  var manifold:b2WorldManifold;
  var collisionNormal:b2Vec2 = new b2Vec2();
 
  canHeroJump = false;
 
  // Iterate through contact lists - all collisions that hero currently have
  for (var edge:b2ContactEdge = hero.GetContactList(); edge; edge = edge.next)
  {
    manifold = new b2WorldManifold();
    edge.contact.GetWorldManifold(manifold);
    collisionNormal = manifold.m_normal;
 
    // We still don't know whether our hero is fixtureA or fixtureB
    var fixtureA:b2Fixture = edge.contact.GetFixtureA();
    var fixtureB:b2Fixture = edge.contact.GetFixtureB();
 
    var nameA:String = fixtureA.GetUserData() ? 
                      (fixtureA.GetUserData() as UserDataInfo).name : "wall";
    var nameB:String = fixtureB.GetUserData() ? 
                      (fixtureB.GetUserData() as UserDataInfo).name : "wall";
 
    // If hero is in fixtureB than the normal is calculated from the colliding 
    // object's point of view so to have consistent normals for hero in 
    // fixture A and B, we multiply it by minus one.
    if (nameB == "hero")
    {
      collisionNormal.x *= -1;
      collisionNormal.y *= -1;
    }
 
    // If we got here it means there is a bounding box collision (AABB).
    // To make sure that shapes are colliding we use IsTouching method.
    if (edge.contact.IsTouching())
    {
      // If the normal vertical value is greater than zero it means that some 
      // object is pushing the hero up. In other words, the hero is standing 
      // in something.
      if (collisionNormal.y > 0)
      {
        canHeroJump = true;
      }
 
      heroNormal = collisionNormal;
    }
  }
}

 

What we are doing is iterating through a contacts list that we get from our hero body object. Each contact object (or ContactEdge) has a pointer to the next one. Why is it called ContactEdge? Well this is a graph structure where edges (lines in a graph) represent contacts and nodes (circles in a graph) are colliding objects. Graphically it looks like on a picture below.

Box2d Collision Graph

You can see how easy it is to read it, the object C collides with the object A and B and on the graph there are two edges linking these three objects from the object C point of view. So each edge has two ends - linking to two objects (nodes) - actually fixtures and you can get them using GetFixtureA and GetFixtureB methods.

Ok, so once we have our edge contact, we create a manifold object and populate it using GetWorldManifold method of the contact object. What the hell is that manifold? Well, each contact stores location of the collision points in local coordinates which is not very useful to us, but we can get global collision points coordinates using these world manifolds. Why do we need point coordinates? This will be used to get collision normals that are very useful and we will see that just in a minute.

So having pointers to our colliding objects (actually their fixtures!), we can check their names using GetUserData. We need to always check which fixture belongs to our hero as this is not guaranteed that FixtureA will always point to the body that we took the contacts from.

As I mentioned before, these fixtures may not be actually colliding, their bounding boxes are but no their circular shapes. This is why we are using IsTouching method that returns true if the shapes are overlapping. In this place we want to set our heroCanJump boolean as well.

To make it easier to understand difference between collision of AABB and collision of fixtures I created two text fields printing all obstacles our hero is colliding with. I also added two new static objects - the circles, move the hero down under them and you will see that even if he is not touching the second circle from the left, it shows up on as a AABB collision. To get a visual reference of bounding boxes around objects, click on the aabbBit button at the top.

You may wonder why the hero standing on or touching one object is sometimes detected twice. Actually the hero object is made of three shapes (fixtures), head, waist and legs and if at any time two or more of them are touching something, the collision is detected for each of them separately.

In addition to know if our hero is touching something, we also want to know if actually he is standing on something. We don't want him to jump if he is in the air already and touches left or right side walls (although some platformers actually do that), we want him to stand on something to jump. This is where we use normals from the manifold object mentioned before. Even if an colliding object has two contact points, both points have the same normal, pointing from fixtureA on fixtureB as on the illustration (from the Box2D manual) below:

Normals

So if our hero is standing on something the y part of the normal should be +1, if there is something pressing him it should be -1. This is true if hero is our fixtureA. If other object colliding with the hero is fixtureA and the hero is fixtureB we need to mutliply the normal by -1.

So this is why we added a check to determine which fixture is our hero and convert normal if needed.

if (nameB == "hero")
{
  collisionNormal.x *= -1;
  collisionNormal.y *= -1;
}

 

Inside the isTouching if clause we check the normal's y part and set canHeroJump variable:

if (collisionNormal.y > 0)
{
  canHeroJump = true;
}

 

Then in the updatePhysics function let's modify the condition for applying impulse to make the hero jump:

if (isKeyDown(Keyboard.UP) && canHeroJump)
{
  hero.ApplyImpulse(new b2Vec2(0, -heroSpeedY / 2), hero.GetWorldCenter());
}

 

Now the impulse will be applied only once, so it won't add new value on top of the current one as before. For this reason we need to increase the maxSpeedY value to 20. And here is the result: (in the text fields I also print the normal value of the collision contact so you can read where is the obstacle colliding with the hero located).

So our simulation looks little more like platformer, the hero only jumps when there is some piece of ground under his legs. But try to jump to the left or right wall pressing the LEFT or RIGHT key, you will notice that the hero stick to it. We don't want that. But we can help it again using our normals, this time the x component of it. If during collision we detect that x has negative value it would mean that the hero is pressing against the left wall, if normal has positive value, the hero presses the right wall. Let's create another global variable called heroNormal, set it equal to collisionNormal only if there is a touch collision and use it as the additional condition when controlling the hero's left and right movements in the update function. You can see it in the final code and here is a result:

 

Creating one-sided platfroms with custom contact listeners

To make the game a little bit more like a platformer, let's create one more platform and make it one sided, so the hero could jump on it through it.

Add this line to the createWorldObjects function:

createBox(120, GAME_HEIGHT - 125, GAME_WIDTH - 250, 10, 
                         false, 1, .5, false, "oneSidedPlatform2");

 

So what does it mean the platform is one-sided? It means that when the hero is below that platform, it could jump on it but when the hero falls on that platform, it will stop him. So how are we going to do make platforms one-sided? We can manipulate contacts so the Box2B engine will not perform default action which is the collision reaction that prevents objects overlapping when the collision is detected.

In order to do that we need to create our own custom contact listener that will extend from the b2ContactListener and register it to the world object using the SetContactListener method.

According to the documentation there are four type of events:

  • Begin Contact Event called when two fixtures begin to everlap
  • Pre-Solve Event called after collision detection but before collision resolution (this is when we can stop resolution from happening)
  • Post-Solve Event called after collision resolution (useful when you want to read collision impulse results)
  • End Contact Event called when two fixtures stop overlapping

 

So first let's create our custom contact listener and use Pre-Solve event to stop collision reaction from happening:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class CustomContactListener extends b2ContactListener
{
  override public function PreSolve(contact:b2Contact, oldManifold:b2Manifold):void 
  {
    var fixtureA:b2Fixture = contact.GetFixtureA();
    var fixtureB:b2Fixture = contact.GetFixtureB();
 
    // If any of the two fixtures doesn't have the user data object then return,
    // as we need 'hero' and 'oneSidedPlatform' objects only.
    if (!fixtureA.GetUserData() || !fixtureB.GetUserData()) return;
 
    var nameA:String = (fixtureA.GetUserData() as UserDataInfo).name;
    var nameB:String = (fixtureB.GetUserData() as UserDataInfo).name;
 
    if (nameA != "hero" && nameB != "hero") return;
    if (nameA.indexOf("oneSidedPlatform") == -1 && nameB.indexOf("oneSidedPlatform") == -1) return;
 
    // Now find out which is hero and which is platform and get their position and height.
    var heroPos:b2Vec2, platformPos:b2Vec2;
    var heroHeight:Number, platformHeight:Number;
 
    if (nameA == "hero")
    {
      heroPos = fixtureA.GetBody().GetPosition();
      heroHeight = (fixtureA.GetUserData() as UserDataInfo).height;
 
      platformPos = fixtureB.GetBody().GetPosition();
      platformHeight = (fixtureB.GetUserData() as UserDataInfo).height;
    }
    else
    {
      platformPos = fixtureA.GetBody().GetWorldCenter();
      platformHeight = (fixtureA.GetUserData() as UserDataInfo).height;
 
      heroPos = fixtureB.GetBody().GetWorldCenter();
      heroHeight = (fixtureB.GetUserData() as UserDataInfo).height;
    }
 
    // If the bottom part of the hero is under a top part of a platform then do not create collision.
    // As the hero position is in the middle of the hero object we need to add half of his height
    // to get the position at the bottom of his legs. Similar for platform but we need to substract
    // half of it's height to get the position of the top of it.
    if (heroPos.y + heroHeight / 2 > platformPos.y - platformHeight / 2)
    {
      contact.SetEnabled(false);
    }
  }
}

 

When PreSolve event handler is called, it receives two objects and the first one - contact - is very useful to us as this is the same type of contact as we used when going through the hero's collisions. From the contact we can get two fixtures. We are only interested in doing something if one fixture is the hero object and other one is one of the one-sided platforms. We use the name property from our UserDataInfo object that is referenced from fixture's userData property. In a real case scenario it would be a better idea to compare types of the objects rather than their names but for the purpose of this article it is fine. When we confirm that this collision happens for the hero and the one-sided platform we identify which is which and also read their current positions and their height. Having these two pairs of information, the last thing to do is to check if the hero bottom part (hero's position plus half of his height) is above or below the top part of a platform (platform's position minus half of its height). If the hero is above then do nothing - the hero will land on the platform. If he is below than set the contact disabled what will stop Box2D from doing collision reaction and the hero will fly through a platform.

So the last bit is to register the object to the world object in the createWorld function:

// Register custom contact listener for one-sided platforms
ourWorld.SetContactListener(new CustomContactListener());

 

And here is how our hero jumps through the platforms:

 

I think this is a good point to end the first part of the Box2D tutorial. In the second part I will cover all various types of joints, how to control objects with mouse, how to use water and how to use your own graphical assets - display list sprites and Starling sprites. I don't want to say when it will be published as it took me almost a year to complete this one, I was so busy with my projects and the full time work and I create all different bits for this article but never had enough spare time to put it together. But hopefully it won't take long. Anyway there are plenty of really good tutorials available on the internet and having your current knowledge of Box2D will make it easier to understand code samples in different languages.

 

Project files

Project files on my GitHub: https://github.com/wsierakowski/Box2D-basics-part1

Links to manuals and useful tutorials

There are plenty of resources for Box2D. Here are the ones I use most often:

 

Please note that whenever I use quote sign for sentences - I'm quoting the Box2D documentation.

I have also learnt a lot from these tutorials:

 

Comments imported from the original article @sierakowski.eu

 
+- +1 #6 David Hertz 2013-03-09 06:46
Please make the second part!!

Regards from Mexico! :lol:
Quote
 
 
 
+- +2 #5 Hector 2013-02-09 13:41
I really enjoyed this great tutorial. Great Job! Thank you!
Quote
 
 
 
+- +2 #4 wojciech 2012-11-15 22:05
Thanks @iforce2d. Your's website is a really great resource of Box2D tutorials!
Quote
 
 
 
+- +3 #3 iforce2d 2012-11-14 21:10
Hey, nice job! You have packed a lot of info in here!
Quote
 
 
 
+- +2 #2 Armin 2012-04-18 11:13
Great tut but is there any chance to post the source code on the github or something similar?
Quote
 
 
 
+- +2 #1 Fuldner 2012-02-18 11:12
Awesome tutorial. Thanks so much for sharing it!
Quote