The idea of Chapter 5 is to understand what is involved in augmenting Processing with sophisticated third-party physics libraries rather laboriously coding your own features (or expecting Processing to already provide everything you could wish for).
The first thing you have to do is install a Processing wrapper for the jbox2d library, which is not part of Processing. It is a Java library that is a compatible extension to Processing.
This isn’t sufficient! You also need jbox2d. You can get that from Google’s jbox2d archive. Once you have the most recent zip file you will find there (dated 2013) downloaded somewhere on your computer, you need to drag jbox2d-2.1.2.3.zip into the Processing window holding your sketch. This is not a very good user interface, but that is the way you do it. You will get a one-line status bar confirmation that adding the zip file worked.
There are some minor but essential changes to the code printed in Chapter 5 that are needed to get it to work with the versions of the libraries you downloaded above. Those changes have been made in the implementation below. Other than those minor changes, the code below is what is in Shiffman’s GitHub repository.
// A Box2D example
import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
class Boundary {
// A boundary is a simple rectangle with x,y,width,and height
float x;
float y;
float w;
float h;
// But we also have to make a body for box2d to know about it
Body b;
Boundary(float x_,float y_, float w_, float h_) {
x = x_;
y = y_;
w = w_;
h = h_;
// Define the polygon
PolygonShape ps = new PolygonShape();
// Figure out the box2d coordinates
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
// We're just a box
ps.setAsBox(box2dW, box2dH);
// Create the body
BodyDef bd = new BodyDef();
bd.type = BodyType.STATIC;
bd.position.set(box2d.coordPixelsToWorld(x,y));
b = box2d.createBody(bd);
// Attached the shape to the body using a Fixture
b.createFixture(ps,1);
}
// Draw the boundary, if it were at an angle we'd have to do something fancier
void display() {
fill(0);
stroke(0);
rectMode(CENTER);
rect(x,y,w,h);
}
}
// A rectangular box
class Box {
// We need to keep track of a Body and a width and height
Body body;
float w;
float h;
// Constructor
Box(float x, float y) {
w = random(4, 16);
h = random(4, 16);
// Add the box to the box2d world
makeBody(new Vec2(x, y), w, h);
}
// This function removes the particle from the box2d world
void killBody() {
box2d.destroyBody(body);
}
// Is the particle ready for deletion?
boolean done() {
// Let's find the screen position of the particle
Vec2 pos = box2d.getBodyPixelCoord(body);
// Is it off the bottom of the screen?
if (pos.y > height+w*h) {
killBody();
return true;
}
return false;
}
// Drawing the box
void display() {
// We look at each body and get its screen position
Vec2 pos = box2d.getBodyPixelCoord(body);
// Get its angle of rotation
float a = body.getAngle();
rectMode(CENTER);
pushMatrix();
translate(pos.x, pos.y);
rotate(-a);
fill(127);
stroke(0);
strokeWeight(2);
rect(0, 0, w, h);
popMatrix();
}
// This function adds the rectangle to the box2d world
void makeBody(Vec2 center, float w_, float h_) {
// Define a polygon (this is what we use for a rectangle)
PolygonShape sd = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w_/2);
float box2dH = box2d.scalarPixelsToWorld(h_/2);
sd.setAsBox(box2dW, box2dH);
// Define a fixture
FixtureDef fd = new FixtureDef();
fd.shape = sd;
// Parameters that affect physics
fd.density = 1;
fd.friction = 0.3;
fd.restitution = 0.5;
// Define the body and make it from the shape
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(center));
body = box2d.createBody(bd);
body.createFixture(fd);
// Give it some initial random velocity
body.setLinearVelocity(new Vec2(random(-5, 5), random(2, 5)));
body.setAngularVelocity(random(-5, 5));
}
}
// A reference to our box2d world
Box2DProcessing box2d;
// A list we'll use to track fixed objects
ArrayList<Boundary> boundaries;
// A list for all of our rectangles
ArrayList<Box> boxes;
void setup() {
size(640,360);
// Initialize box2d physics and create the world
box2d = new Box2DProcessing(this);
box2d.createWorld();
// We are setting a custom gravity
box2d.setGravity(0, -10);
// Create ArrayLists
boxes = new ArrayList<Box>();
boundaries = new ArrayList<Boundary>();
// Add a bunch of fixed boundaries
boundaries.add(new Boundary(width/4,height-5,width/2-50,10));
boundaries.add(new Boundary(3*width/4,height-50,width/2-50,10));
}
void draw() {
background(255);
// We must always step through time!
box2d.step();
// Boxes fall from the top every so often
if (random(1) < 0.2) {
Box p = new Box(width/2,30);
boxes.add(p);
}
// Display all the boundaries
for (Boundary wall: boundaries) {
wall.display();
}
// Display all the boxes
for (Box b: boxes) {
b.display();
}
// Boxes that leave the screen, we delete them
// (note they have to be deleted from both the box2d world and our list
for (int i = boxes.size()-1; i >= 0; i--) {
Box b = boxes.get(i);
if (b.done()) {
boxes.remove(i);
}
}
}