Bodies are the fundamental objects in a physics scene, but they are not what you actually see bouncing around and colliding with each other. Sound confusing? Hold on, I'll explain.

You can think of a body as the properties of an object that you cannot see (draw) or touch (collide with). These invisible properties include:

  • mass - how heavy it is
  • linear velocity - how fast and which direction it's moving
  • rotational inertia - how much effort it takes to start or stop spinning
  • angular velocity - how fast and which way it's rotating
  • transform - where it is and which way it is facing

Even if you know all of the characteristics of an object, you still don't know what it looks like or how it will react when it collides with another object. To define the size and shape of an object we need to use Shapes, which are the subject of the next topic in this series, so for now we will just use a simple box and cover more details later. Let's create a body and try setting some basic properties of it to see how they work.

There are three types of body available: static, dynamic and kinematic. The first two of these should be readily understandable, while the last one is probably not so intuitive. I'll cover the details a little further along. First we will make a dynamic body so we can see something moving around the scene, and we can try setting the velocity etc. for it.

Creating a body

Bodies are made by first setting up a definition, and then using this to create the body object itself. This can be handy if you want to make many bodies which are all the same, or very similar. Add the following code to create a body definition:

const bodyDef = b2DefaultBodyDef();
bodyDef.type = b2BodyType.b2_dynamicBody; // this will be a dynamic body
bodyDef.position = new b2Vec2(0, 20); // set the starting position
bodyDef.rotation = b2MakeRot(0); // set the starting rotation

That's enough to define a basic body definition. Remember a body does not have any size or shape, so we don't define those here. You may be wondering why it has no mass yet - the usual way of providing a mass for a body is by adding shapes to it, which is coming up in the next step. Now, use this definition to create the actual body:

const dynamicBodyId = b2CreateBody(worldId, bodyDef);

To give a body its size, shape, and other tangible characteristics, we add Shapes to it. Also, the default behaviour is that adding shapes will affect the mass of the body too. A body can have many shapes attached to it, each shape added will affect the total mass of the body. For now, let's add one simple shape to this body, a square:

const shapeDef = b2DefaultShapeDef();
shapeDef.density = 1;

const boxShape = b2MakeBox(1, 1); // Create a 2x2 box
const boxShapeId = b2CreatePolygonShape(dynamicBodyId, shapeDef, boxShape);

A box-shaped dynamic body

Setting body properties

Now let's see what happens when we set some of the properties mentioned at the beginning of this topic. For example, change the starting position and angle:

b2Body_SetTransform(dynamicBodyId, new b2Vec2(10, 20), b2MakeRot(1));

This will make the body start 10 units further to the right, 20 units higher, and rotated 1 radian counter-clockwise. Box2D uses radians for angle measurements, so if you are like me and more used to thinking in angles, you could do something like this:

const DEGTORAD = 0.0174532925199432957; // which is PI / 180
const RADTODEG = 57.295779513082320876; // and this is 180 / PI

b2Body_SetTransform(dynamicBodyId, new b2Vec2(10, 20), b2MakeRot(45 * DEGTORAD)); // 45 degrees counter-clockwise

We can also set the linear velocity and angular velocity of the body:

b2Body_SetLinearVelocity(dynamicBodyId, new b2Vec2(-5, 5)); // moving up and left 5 units per second
b2Body_SetAngularVelocity(dynamicBodyId, -90 * DEGTORAD); // 90 degrees per second clockwise

Static bodies

Now let's see what a static body does. Since we already have definitions for a body and a shape, we can re-use them and just change the necessary features:

bodyDef.type = b2BodyType.b2_staticBody; // this will be a static body
bodyDef.position = new b2Vec2(0, 10); // slightly lower position
const staticBodyId = b2CreateBody(worldId, bodyDef);
b2CreatePolygonShape(staticBodyId, shapeDef, boxShape);

A dynamic box colliding with a static box

Kinematic bodies

Lastly, let's see what a 'kinematic' body is all about. As we have seen so far, dynamic bodies move and static bodies don't. When a static body and a dynamic body collide, the static body always 'wins' and holds its ground, and the dynamic body will retreat as necessary so that the two are not overlapping. A kinematic body is very similar to a static body in that when it collides with a dynamic body it always holds its ground and forces the dynamic body to retreat out of the way. The difference is that a kinematic body can move.

Try setting up a kinematic body like this:

bodyDef.type = b2BodyType.b2_kinematicBody; // this will be a kinematic body
bodyDef.position = new b2Vec2(-18, 11); // start from left side, slightly above the static body
const kinematicBodyId = b2CreateBody(worldId, bodyDef);
b2CreatePolygonShape(kinematicBodyId, shapeDef, boxShape);

b2Body_SetLinearVelocity(kinematicBodyId, new b2Vec2(1, 0)); // move right 1 unit per second
b2Body_SetAngularVelocity(kinematicBodyId, 360 * DEGTORAD); // 1 turn per second counter-clockwise

A rotated kinematic box

Getting body properties

Often you will want to know where a body is or how fast it is moving, rotating etc. This is pretty easy so lets give it a try:

const pos = b2Body_GetPosition(dynamicBodyId);
const rotation = b2Body_GetRotation(dynamicBodyId); // this is a b2Rot
const angle = b2Rot_GetAngle(rotation); // convert a b2Rot into an angle
const vel = b2Body_GetLinearVelocity(dynamicBodyId);
const angularVel = b2Body_GetAngularVelocity(dynamicBodyId);

console.log(`Position: ${pos.x.toFixed(3)},${pos.y.toFixed(3)} Angle: ${(angle * RADTODEG).toFixed(3)}`);
console.log(`Velocity: ${vel.x.toFixed(3)},${vel.y.toFixed(3)} Angular velocity: ${(angularVel * RADTODEG).toFixed(3)}`);

You can feed the GetPosition() and GetRotation() results back into SetTransform so that no change is made. For example, the following code will cause no change to a body's movement at all:

b2Body_SetTransform(bodyId, b2Body_GetPosition(bodyId), b2Body_GetRotation(bodyId));

Iterating over the bodies in the world

If you want to look at all the bodies in the world, you can use the world's body array:

// Get array of all body IDs in the world
const bodies = world.bodyArray;
for (const bodyId of bodies) {
    // do something with the body
    if (b2Body_IsValid(bodyId)) {
        // body is still valid
    }
}

Cleaning up

When you're done with a body, you can remove it from the scene by calling DestroyBody:

b2DestroyBody(dynamicBodyId);

When a body is destroyed like this, it takes care of deleting all the shapes and joints attached to it. Remember not to use the deleted body ID after this!

Credits

This tutorial is adapted from an original piece of work created by Chris Campbell and is used under license.