(This is a work-in-progress excerpt from what will hopefully be my upcoming Nature of Code book.)

       

View online examples: Falling Boxes, Bumpy Surface, Blob, Mouse Interaction, Particle System with Box2D

You can download the PBox2D library and all examples via the Google Code PBox2D repository.

The Nature of Box2D

Up until now, we’ve been following a fairly linear narrative.

1) Learn about a concept from the world of physics — What is a vector? What is a force? What is a wave? Etc.

2) Understand the math and algorithms behind such concept.

3) Implement the algorithms in Processing with an object-oriented approach.

This has yielded a set of motion simulation examples, allowing us to creatively define the physics of the worlds we build (whether realistic or fantastical). Of course, we aren’t the first to try this. The world of computer graphics and programming is full of source code dedicated to simulation. Just try googling “open source physics engine” and spend the rest of your day pouring through rich and complex code. While many of the libraries out there give us physics (and super awesome advanced physics at that) for free, in my view, it’s key that we started with learning the fundamentals from scratch before diving into libraries. For one, without an understanding of vectors, forces, and trigonometry, we’d be completely lost even just reading the documentation of a library. And two, as wonderful a physics engine can be, if you look deep down into your hearts, it’s likely that you seek to create worlds and visualizations that stretch the limits of imagination. A library is great, but it is a limited set of features. It’s important to know both when to live within the limitations in the pursuit of a Processing project and when those limits prove to be confining.

This tutorial serves as a basic introduction to using the Box2D physics engine in Processing.

What is Box2D?

Box2D is an open source C++ physics engines (with many ports to other environments). To learn more, here are a list of links:

If you take a look at the JBox2D page, you’ll notice a collection of terrific demos with a happy little line of text below: “All demos use Processing for graphics rendering.” Awesome! JBox2D must work with Processing! And so we could just stop here. I could point you to that link and say “have fun!” Nevertheless, while the demos are excellent, if you download the source, you’ll notice that it’s a large collection of many many java files. It’s not terribly accessible for the beginner Processing programmer. My goal here is to create a series of simple examples that, although at the moment not as comprehensive as what you will find on jbox2d.org, will help make getting started with Box2D a bit easier.

Is this a Processing Box2D library or a set of Processing examples that use JBox2D?

I have to admit, I’m not entirely sure what I’m doing here. I know I want to make using Box2D easier in Processing, but I also don’t want to hide too much about Box2D. Users need to both take beginner steps as well as dive into the deep expanse of features that is Box2D. As of this version, I’ve got something of a compromise. There is a Processing library (called PBox2D) which is just a little helper class. It sets up your Box2D world for you and provides some functions that you generally have to call over and over again. But to do the real work in your Processing code, you still have to refer to the JBox2D reference and make JBox2D objects.

Three Primary Box2D Concepts

1. World

Manual page: World (note this is the C++ manual).

You Box2D World manages everything inside the world of your simulation. It stores lists of all the bodies present in the world as well as any joints connecting those bodies. When you create a world, you need 3 parameters:

  • AABB or the “axis-aligned bounding box” — This is really just a fancy term for the box that defines the edges of your universe. It’s important because Box2D will ignore anything that exists outside this bounding box.
  • Gravity — When you make a world, you must define a gravity vector, i.e. how strong is gravity and what is gravity’s direction. Gravity doesn’t have to be fixed; you can adjust the gravity vector only the fly while your program is running.
  • doSleep — This is a boolean argument you must pass when creating a world. If you set it to true, it tells the world to allow bodies to sleep when they come to rest. It’s pretty standard to leave this on, but you do have to watch out and sometimes need to “wake up” your objects.

Knowing the above 3 concepts, we can create a Box2D world in our code like so:

AABB worldAABB = new AABB();
worldAABB.lowerBound.set(-100,-100);
worldAABB.upperBound.set(100,100);
Vec2 gravity = new Vec2(0.0, -10.0);
boolean doSleep = true;
World world = new World(worldAABB, gravity, doSleep);

We’re not actually going to include the above code in our sketches, it’s taken care of by the PBox2D helper class. But by examining how a world is created, we’ve opened up two important points regarding working with Box2D.

  1. The Box2D coordinate system is not your pixel coordinate system!! The engine is not tuned for large numbers so if you have a full screen 1024×768 window, everything is going to have to be translated and scaled to Box2D coordinates so that the physics work properly and then back again when it comes time to actually draw something on the screen. Fortunately, the PBox2D helper library takes care of converting between pixel space and Box2D space for you, as we’ll demonstrate in a moment.
  2. We can’t use PVector. Box2D comes with its own vector class: Vec2. It’s right there in the above demo code: Vec2 gravity = new Vec2(0,-10). The good news is that conceptually it’s still just a vector and we’re going to use it in all the same ways as we used PVector. The bad news is some of the syntax will be different, which we’ll see when we look at example code.

Let’s learn how we can convert coordinates from Box2D (“world”) to Processing (“screen”) and vice-versa. But before we do this,let’s quickly return to creating the Box2D world using the PBox2D helper class.

// Create a Box2D object
PBox2D box2d = new PBox2D(this);
// Create a "default" world (we can do more here, but for now this will do)
box2d.createWorld();

Once we have our world, we can take Processing coordinates and turn them into Box2D world vectors and vice versa.

Convert mouseX,mouseY to coordinate in Box2D world:

Vec2 mouseWorld = box2d.screenToWorld(mouseX,mouseY);

Convert a Box2D world position to Processing screen space:

// Let's assume we have a worldPosition vector, i.e.:
Vec2 worldPosition = new Vec2(-10,25);
Vec2 screenPosition = box2d.worldToScreen(worldPosition);
// We need the "screen position" if we are going to draw something on the screen!
ellipse(screenPosition.x,screenPosition.y,16,16);

Incidentally, Box2D uses real-world measurements in its calculations (meters, kilograms, seconds, etc.) Box2D knows nothing about computer graphics and rendering, it is truly a pure physics engine that just deals with numbers. You give it numbers and you get numbers back. For example, if you say: box2D.setGravity(0,-9.8), you are using real world gravity!

2. Body

Manual page: Bodies

A Box2D body is the primary element in the Box2D world. It has a location. It has a velocity. Sound familiar? The Body is essentially the class we’ve been writing on our own in our vectors and forces examples. A Box2D body can also experience forces. It can also be static (meaning fixed and not moving). It’s important to note, however, that a Body is not a Shape. Bodies carry Shapes around with them (this way a Body can be a single rectangle or a rectangle attached to a circle, etc.) We’ll look at Shapes in a moment, for now let’s see how we make a Body.

The first thing we have to do is create a “Body Definition.” This will let us define the properties about the Body we intend to make.

BodyDef bd = new BodyDef();

Next we can set properties. For example, we might want to position the Body in the center of the Processing window. Remember, if we are going to pass a coordinate to the Body Definition, it must be a Box2D world coordinate, not a screen one!

Vec2 center = box2d.screenToWorld(width/2,height/2));
bd.position.set(center);

Once we’re done with the definition (BodyDef), we can create the Body itself:

// Create the body!
Body body = box2d.createBody(bd);

And once we have the body, we can set some more properties, such as initial velocity:

// Give it some initial random velocity
body.setLinearVelocity(new Vec2(random(-5,5),random(2,5)));
body.setAngularVelocity(random(-5,5));
3. Shapes

Manual page: Shapes

A Body on its own isn’t anything that physically exists in the world. It’s like a soul with no human form to inhabit. For a Body to have mass, we have to define a Shape and attach that Shape to the Body. Shapes keep track of all the necessary collision geometry attached to a Body. And by specifying a Shape’s density, your Body has mass. Finally, Shapes also have friction and restitution (“bounciness”). One of the nice things about separating the concepts of Bodies and Shapes is that you can attach multiple shapes to a single Body in order to create more complex forms.

Much like with a Body, to create a Shape, we need to first create a ShapeDef instance. For most non-circular shapes, a PolygonDef will work just fine. For example, let’s look at how we define a rectangle.

// Define the shape -- a polygon (this is what we use for a rectangle)
PolygonDef sd = new PolygonDef();

Next up, we have to define the width and height of the rectangle. Let’s say we want our rectangle to be 150×100 pixels. Remember, pixel units are no good for Box2D shapes! So we have to use our helper functions to convert them first.

// Scale to Box2D coordinates
float w = box2d.scaleScreenToWorld(150);
float h = box2d.scaleScreenToWorld(100);
// Define the Shape as "Box" (rectangle)
sd.setAsBox(w, h);

Finally, we can set the Shape’s properties:

// Parameters that affect physics
sd.density = 1.0;     // A density of 0 will create a fixed Body that doesn't move
sd.friction = 0.3;    // How much friction
sd.restitution = 0.5; // How bouncy is this Shape?

Once the Shape is defined, all we have left to do is attach the Shape to the Body:

// Attach that shape to our body!
body.createShape(sd);
body.setMassFromShapes();

Note the final function called is setMassFromShapes(). This tells Box2D I’m finished making the Body and you can figure out its mass from all of the attached Shapes (in this case just one). You aren’t limited to calling this function just once. Since you can create or destroy shapes on a body on the fly, you can always call setMassFromShapes() again to adjust the mass accordingly. Also, if you don’t want Box2D to calculate the mass for you (based on the Shape’s density), you can use setMass() instead.

Oh, and in case you were wondering, if your Shape is a circle, you can use a CircleDef:

// Make the body's shape a circle
CircleDef cd = new CircleDef();
// Don't forget to scale the radius!
cd.radius = box2d.scaleScreenToWorld(10);
cd.density = 1.0;
cd.friction = 0.01;
cd.restitution = 0.3;
body.createShape(cd);
body.setMassFromShapes();

Putting the Body all together

Let’s review all the steps we took to construct a Body.

  1. Define a Body using BodyDef (properties such as location can be set)
  2. Create the Body from the Body Definition.
  3. Define a Shape using PolygonDef, CircleDef, or any other Shape definition class (properties such as friction, density, and restitution can be set.
  4. Attach the Shape to the Body
  5. Update the Body’s mass

And here’s the code:

// STEP 1: DEFINE BODY
BodyDef bd = new BodyDef();
bd.position.set(box2d.screenToWorld(width/2,height/2));

// STEP 2: CREATE BODY
Body body = box2d.createBody(bd);

// STEP 3: DEFINE SHAPE
PolygonDef sd = new PolygonDef();
float w = box2d.scaleScreenToWorld(150);
float h = box2d.scaleScreenToWorld(100);
sd.setAsBox(w, h);
// Parameters that affect physics
sd.density = 1.0;
sd.friction = 0.3;
sd.restitution = 0.5;

// STEP 4: ATTACH SHAPE TO BODY
body.createShape(sd);

// STEP 5: UPDATE BODY'S MASS
body.setMassFromShapes();

Keeping Track of it all

Once a body is made it lives in the Box2D physics world. Box2D will always know it’s there, check it for collisions, move it appropriately according the forces, etc. It’ll do all that stuff for you without you having to lift a finger! The thing that it won’t do, however, is display the Body for you. This is a good thing. This is your time to shine. When working with Box2D what we’re essentially saying is “I want to be the designer of my world, and I want you, Box2D, to compute all the physics.”

Now, Box2D will keep track of a list of all the Bodies that exist in the world for you. This can be accessed by calling the World object’s getBodyList() function. Nevertheless, what I’m going to demonstrate here is a technique for keeping your own Body lists. Yes, this may be a bit redundant and we perhaps sacrifice a bit of efficiency. But we more than make up for that with ease of use. This methodology will allow us to program like we’re used to in Processing and easily keep track of which Bodies are which and render them appropriately. Let’s consider the structure of the following Processing sketch:

This looks like any ol’ Processing sketch. We have a main tab called “Boxes” and a “Boundary” and “Box” tab. Let’s think about the Box tab for a moment. There, we write a class to describe a Box object, a simple class with a Constructor and a function called display() that draws the rectangle.

// A rectangular box
class Box  {

  float x,y;
  float w,h;

  // Constructor
  Box(float x_, float y_) {
    x = x_;
    y = y_;
    w = random(4,16);
    h = random(4,16);
  }

  // Drawing the box
  void display() {
    fill(175);
    stroke(0);
    rect(x,y,w,h);
  }
}

Our main tab might have a simple program that creates Box objects when the mouse is pressed and draws them on the screen.

// A list for all of our rectangles
ArrayList boxes;

void setup() {
  size(400,300);
  smooth();
  // Create ArrayLists
  boxes = new ArrayList();
}

void draw() {
  background(255);

  // When the mouse is clicked, add a new Box object
  if (mousePressed) {
    Box p = new Box(mouseX,mouseY);
    boxes.add(p);
  }

  // Display all the boxes
  for (int i = 0; i < boxes.size(); i++) {
    Box p = (Box) boxes.get(i);
    p.display();
  }
}

Now, here’s our assignment. Take the above example verbatim, but instead of simply drawing fixed boxes on the screen, draw boxes that experience physics (via Box2D) as soon as they appear. We’ll need two major steps to accomplish our goal.

1. Add Box2D to our main program (i.e. setup() and draw())

This part is not too tough. We can use the PBox2D helper class and create a PBox2D object, and in setup() initialize it.

// A reference to our box2d world
PBox2d box2d;

void setup() {
  // Initialize box2d physics and create the world
  box2d = new PBox2d(this);
  box2d.createWorld();
  // We are setting a custom gravity
  box2d.setGravity(0, -20);
}

Then in draw(), we need to make sure we call one very important function: step(). Without this function, nothing would ever happen! step() takes the box2d world one step further through time. Internally, the engine sweeps through and looks at all of the bodies and figures out what to do with them. Just calling step() on its own moves the Box2D world forward with default settings, however, it is customizable (check PBox2D source documentation for more).

void draw() {
  // We must always step through time!
  box2d.step();
}

2. Store a reference to a Box2D Body object inside of our Box class

Before, our Box class just included fields for location and width and height. Now, for every Box object we create, we also want to create a Body object for the Box2D world, i.e.

// A rectangular box
class Box  {

  // We need to keep track of a Body and a width and height
  Body body;

  float w;
  float h;

We don’t need (x,y) anymore since, as we’ll see, the Body itself will keep track of its location. Then in our constructor, in addition to initializing width and height, we go ahead and include all of the Body and Shape code we learned above!

  // Constructor
  Box(float x, float y) {

    w = random(4,16);
    h = random(4,16);

    // Define a polygon (this is what we use for a rectangle)
    PolygonDef sd = new PolygonDef();
    float box2dW = box2d.scaleScreenToWorld(w/2);
    float box2dH = box2d.scaleScreenToWorld(h/2);
    sd.setAsBox(box2dW, box2dH);

    // Parameters that affect physics
    sd.density = 1.0;
    sd.friction = 0.3;
    sd.restitution = 0.5;

    // Define the body and make it from the shape
    BodyDef bd = new BodyDef();
    Vec2 center = new Vec2(x,y);
    bd.position.set(box2d.screenToWorld(center));

    body = box2d.createBody(bd);
    body.createShape(sd);
    body.setMassFromShapes();

    // Give it some initial random velocity
    body.setLinearVelocity(new Vec2(random(-5,5),random(2,5)));
    body.setAngularVelocity(random(-5,5));
 }
 

Ok, we’re almost there. Before we introduce Box2D, it was easy to display an object. The object’s location was stored in variables x and y and we called rect().

  // Drawing the box
  void display() {
    fill(175);
    stroke(0);
    rect(x,y,w,h);
  }
 

But now Box2D manages the object’s motion. So we can no longer use our own variables to display the shape. But not to fear! Our Box object has a reference to the Box2D Body associated with it. So all we need to do is ask the Body for its location. Since this is a task we’ll need to do quite often, it is taken care of for us by the PBox2D helper:

Vec2 pos = box2d.getScreenPos(body);

However, let’s take a moment to see how we would do it longhand (without the helper function).

XForm xf = body.getXForm();
Vec2 pos = box2d.worldToScreen(xf.position);

An XForm object describes the transformation state (i.e. location and rotation) of a Body. We can pull out information from it and then using our worldToScreen() function, translate its Box2D location to screen location.

Finally, we can ask the body for its angle of rotation:

// Get its angle of rotation
float a = body.getAngle();

Once we have the location and angle, it’s easy to display the object using translate and rotate!

  // Drawing the box
  void display() {
    // We look at each body and get its screen position
    Vec2 pos = box2d.getScreenPos(body);
    // Get its angle of rotation
    float a = body.getAngle();

    rectMode(CENTER);
    pushMatrix();
    translate(pos.x,pos.y);
    rotate(a);
    fill(175);
    stroke(0);
    rect(0,0,w,h);
    popMatrix();
  }

In case we want to have objects that can leave the physics, it’s often useful to also include a function to destroy a Body, i.e.

  // This function removes the particle from the box2d world
  void killBody() {
    box2d.destroyBody(body);
  }

Coming Next

  • Demonstrate how to apply forces to objects
  • Demonstrate how to interact with the mouse
  • Show some advanced structures — chains, bridges, blobs, curvy edges, compound shapes
  • Detecting collisions with ContactListener
  • What else? Requests?

No Responses to “Box2D + Processing”  

  1. No Comments

Leave a Reply