PBox2D
This presentation is based on the underline ppt file
http://pds21.egloos.com/pds/201304/09/15/Class4-PhysicalLibrary.pptx
2013.5.16
[email protected]
3
5
http://en.wikipedia.org/wiki/Box2D 7
https://code.google.com/p/box2d/ 9
Box2D Testbed
11
https://code.google.com/p/box2dweb/ 13
iforce2d advanced topics
http://www.iforce2d.net/b2dtut/ 15
http://paralaxer.com/box2d-physics/ 17
http://www.jangaroo.net/files/examples/flash/box2d/ 19
http://www.mrdoob.com/projects/chromeexperiments/google-gravity/ 21
http://natureofcode.com/book/chapter-5-physics-libraries/ 23
What is the Box2D?
Open-Source 2D Physics Engine
C++ by Erin Catto for the Game Developer’s Conference in 2006.
Box2D site (http://www.box2d.org/) for reference
Crayon Physics
Angry Birds
JBox2D : Java Wrapper
Box2D site (http://www.jbox2d.org//) for reference.
Can be directly used in Processing
PBox2D : Processing Wrapper
Make it easy to use JBox2D
A layer library between JBox2D and Processing
a Processing Box2D “helper” library
PBox2D is not a Processing wrapper for all of Box2D
https://github.com/shiffman/PBox2D
Box2D Basics
In old physical simulation
SETUP:
Create all the objects in our world.
DRAW:
Calculate all the forces in our world.
Apply all the forces to our objects (F = M * A).
Update the locations of all the objects based on their acceleration.
Draw all of our objects.
In Box2D
SETUP:
Create all the objects in our world.
DRAW:
Draw all of our objects.
25
Five core parts for using Box2D
1. World : Manages the physics simulation. It knows everything about the overall coordinate space and also stores lists of every element in the world
2. Body : Serves as 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.
3. Shape : Keeps track of all the necessary collision geometry attached to a body.
4. Fixture : Attaches a shape to a body and sets properties such as density, friction, and restitution.
5. Joint : Acts as a connection between two bodies (or between one body
and the world itself).
A new Vector Class : Vec2
27
PVector Vec2
PVector a = new PVector(1,-1);
PVector b = new PVector(3,4);
a.add(b);
Vec2 a = new Vec2(1,-1);
Vec2 b = new Vec2(3,4);
a.addLocal(b);
PVector a = new PVector(1,-1);
PVector b = new PVector(3,4);
PVector c = PVector.add(a,b);
Vec2 a = new Vec2(1,-1);
Vec2 b = new Vec2(3,4);
Vec2 c = a.add(b);
PVector a = new PVector(1,-1);
float n = 5;
a.mult(n);
Vec2 a = new Vec2(1,-1);
float n = 5;
a.mulLocal(n);
PVector a = new PVector(1,-1);
float n = 5;
PVector c = PVector.mult(a,n);
Vec2 a = new Vec2(1,-1);
float n = 5;
Vec2 c = a.mul(n);
PVector a = new PVector(1,-1);
float m = a.mag();
a.normalize();
Vec2 a = new Vec2(1,-1);
float m = a.length();
a.normalize();
Box2D worlds
How can we convert between them?
Helper functions
29
Task Function
Convert location from World to Pixels Vec2 coordWorldToPixels(Vec2 world)
Convert location from World to Pixels Vec2 coordWorldToPixels(float worldX, float worldY) Convert location from Pixels to World Vec2 coordPixelsToWorld(Vec2 screen)
Convert location from Pixels to World Vec2 coordPixelsToWorld(float pixelX, float pixelY) Scale a dimension (such as height, width, or radius) from
Pixels to World
float scalarPixelsToWorld(float val)
Scale a dimension from World to Pixels float scalarWorldToPixels(float val)
Set up the world
PBox2D box2d;
void setup() {
box2d = new PBox2D(this);
//Initializes a Box2D world with default settings box2d.createWorld();
}
Set up the Body
Step 1: Define a body.
31
//define the properties of the body we intend to make BodyDef bd = new BodyDef();
Vec2 center = box2d.coordPixelsToWorld(width/2,height/2));
bd.position.set(center);
bd.fixedRotation = true; //Fixed, never rotating objects bd.linearDamping = 0.8; //Friction
bd.angularDamping = 0.9;
bd.bullet = true; //if it is FAST moving objects bd.type = BodyType.DYNAMIC; // body type
Step 2: Configure the body definition.
Damping
A Damping node can be used to slow down a body (a Solid node with Physics). The speed of each body is reduced by the specified amount (between 0.0 and 1.0) every second. A value of 0.0 means
"no slowing down" and value of 1.0 means a
"complete stop", a value of 0.1 means that the speed should be decreased by 10 percent every second. A damped body will possibly come to rest and become disabled depending on the values specified in WorldInfo. Damping does not add any force in the simulation, it directly affects the
velocity of the body. The damping effect is applied after all forces have been applied to the bodies.
Damping can be used to reduce simulation
instability.
Body types
Dynamic (BodyType.DYNAMIC)
a “fully simulated” body. A dynamic body moves around the
world, collides with other bodies, and responds to the forces in its environment.
Static (BodyType.STATIC)
cannot move (as if it had an infinite mass). We’ll use static bodies for fixed platforms and boundaries.
Kinematic(BodyType.KINEMATIC)
can be moved manually by setting its velocity directly. If you have a user-controlled object in your world, you can use a kinematic body. Note that kinematic bodies collide only with dynamic
bodies and not with other static or kinematic ones.
33
Body body = box2d.createBody(bd);
Step 3: Create the body.
Set up the Body
Step 4: Set any other conditions for the body’s starting state.
body.setLinearVelocity(new Vec2(0,3));
body.setAngularVelocity(1.2);
Linking Shape with Body
Body does not have geometry
We have to attach geometry shape to body with a Fixture
We can attach multiple shapes to a single body in order to create more complex forms.
Step 1: Define a shape
35
PolygonShape ps = new PolygonShape();
float box2Dw = box2d.scalarPixelsToWorld(150);
float box2Dh = box2d.scalarPixelsToWorld(100);
ps.setAsBox(box2Dw, box2Dh);
Width & Height
width height
height
width
Processing BOX2D
Step 2: Create a fixture.
FixtureDef fd = new FixtureDef();
//The fixture is assigned the PolygonShape we just made.
fd.shape = ps;
fd.friction = 0.3; //The coefficient of friction for the shape, typically between 0 and 1
fd.restitution = 0.5; //The Shape’s restitution (i.e. elasticity), typically between 0 and 1
fd.density = 1.0; //The Shape’s density, measured in kilograms per meter squared
Step 3: Attach the shape to the body with the fixture.
body.createFixture(fd);
Or you can use the default fixture
body.createFixture(ps,1); // Creates the Fixture and attaches the Shape with a density of 1
37
Friction
Restitution(Elasticity)
39
Density
In summary
1. Define a body using a BodyDef object (set any properties, such as location).
2. Create the Body object from the body definition.
3. Define a Shape object using PolygonShape, CircleShape, or any other shape class.
4. Define a fixture using FixtureDef and assign the fixture a shape (set any properties, such as friction, density, and restitution).
5. Attach the shape to the body.
41
Shapes
Circle Shapes
Polygon Shapes
b2CircleShape circle;
circle.m_p.Set(2.0f, 3.0f);
circle.m_radius = 0.5f;
// This defines a triangle in CCW order.
b2Vec2 vertices[3];
vertices[0].Set(0.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 1.0f);
int32 count = 3;
b2PolygonShape polygon;
polygon.Set(vertices, count);
Shapes
Edge Shapes
43
// This an edge shape with ghost vertices.
b2Vec2 v0(1.7f, 0.0f);
b2Vec2 v1(1.0f, 0.25f);
b2Vec2 v2(0.0f, 0.0f);
b2Vec2 v3(-1.7f, 0.4f);
b2EdgeShape edge;
edge.Set(v1, v2);
edge.m_hasVertex0 = true;
edge.m_hasVertex3 = true;
edge.m_vertex0 = v0;
edge.m_vertex3 = v3;
Shapes
Chain Shapes
// This a chain shape with isolated vertices b2Vec2 vs[4];
vs[0].Set(1.7f, 0.0f);
vs[1].Set(1.0f, 0.25f);
vs[2].Set(0.0f, 0.0f);
vs[3].(-1.7f, 0.4f);
b2ChainShape chain;
chain.CreateChain(vs, 4);
In processing term.
45 //Step 1. Define the body.
BodyDef bd = new BodyDef();
bd.position.set(box2d.coordPixelsToWorld(width/2,height/2));
//Step 2. Create the body.
Body body = box2d.createBody(bd);
//Step 3. Define the shape.
PolygonShape ps = new PolygonShape();
float w = box2d.scalarPixelsToWorld(150);
float h = box2d.scalarPixelsToWorld(100);
ps.setAsBox(w, h)
//Step 4. Define the fixture.
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.density = 1;
fd.friction = 0.3;
fd.restitution = 0.5;
//Step 5. Attach the shape to the body with the Fixture.
body.createFixture(fd);
How can we keep tracking objects?
Use the arrayList (Dynamic array)
Exercise
Making boxes on the fly whenever we move the box and put it on an arrayList.
// A list for all of our rectangles ArrayList<Box> boxes;
void setup() { size(800,200);
smooth();
// Create ArrayLists
boxes = new ArrayList<Box>();
}
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 (Box b: boxes) { b.display();
} }
// A rectangular box class Box {
float x,y;
float w,h;
// Constructor
Box(float x_, float y_) { x = x_;
y = y_;
w = 16;
h = 16;
}
// Drawing the box void display() { fill(127);
stroke(0);
strokeWeight(2);
rectMode(CENTER);
rect(x,y,w,h);
} }
Class Design
47
Let’s put those boxes on the Box2D world
Revise the Box class
Box size (8,8)
fd.density = 1;
fd.friction = 0.3;
fd.restitution = 0.5;
class Box { Body body;
float w,h;
Box(float x, float y) { //initial position w = 16;
h = 16;
}
display() {
Vec2 pos = box2d.getBodyPixelCoord(body);
float a = body.getAngle();
pushMatrix();
translate(pos.x,pos.y);
// Box2D coordinate system considers rotation in the opposite direction from Processing rotate(-a);
fill(127);
stroke(0);
strokeWeight(2);
rectMode(CENTER);
rect(0,0,w,h);
popMatrix();
} } // following the steps
// Step 1. Define the body.
// Step 2. Create the body.(bodyType=DYNAMIC) // Step 3. Define the shape.
// Step 4. Define the fixture.
// Step 5. Attach the shape to the body with the Fixture
Main function
49
import pbox2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
// A list for all of our rectangles ArrayList<Box> boxes;
PBox2D box2d;
void setup() { size(800, 200);
smooth();
// Initialize and create the Box2D world box2d = new PBox2D(this);
box2d.createWorld();
// Create ArrayLists
boxes = new ArrayList<Box>();
}
void draw() { background(255);
// We must always step through time!
box2d.step();
// When the mouse is clicked, add a new Box object Box p = new Box(mouseX, mouseY);
boxes.add(p);
// Display all the boxes for (Box b: boxes) { b.display();
} }
Let’s put some boundaries.
To create boundaries,
we need to set BodyDef object’s type to STATIC.
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_;
}
// 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);
} }
Let’s design Boundary class
// following the steps // Step 1. Define the body.
// Step 2. Create the body.(bodyType=DYNAMIC) // Step 3. Define the shape.
// Step 4. Define the fixture.
// Step 5. Attach the shape to the body with the Fixture
51
// A reference to our box2d world PBox2D 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(800, 200);
smooth();
// Initialize and create the Box2D world box2d = new PBox2D(this);
box2d.createWorld();
// 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));
}
Let’s design main function
One problem of this example
Computationally expensive as we increase the number of boxes
It is getting slow as it goes.
How can we fix it?
If the box is out of screen, remove it!!
53
void killBody() {
box2d.destroyBody(body);
//ask box2d to destroy the 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+h) { killBody();
return true;
}
return false;
}
Let’s modify Box class and main function
for (int i = boxes.size()-1; i >= 0; i--) { Box b = boxes.get(i);
if (b.done()) {
boxes.remove(i); //remove from the array }
}
BOX class Draw() in main function
Curvy Boundary
If you want a fixed boundary that is a curved surface (as opposed to a polygon)
Use the ChainShape
55
polygonShape : Convex moving object
chainShape : static game world
Steps
Step 1: Define a body.
57
BodyDef bd = new BodyDef();
Body body = box2d.world.createBody(bd);
Step 2: Define the Shape.
ChainShape chain = new ChainShape();
Step 3: Configure the Shape.
Vec2[] vertices = new Vec2[2];
vertices[0] = box2d.coordPixelsToWorld(0,150);
vertices[1] = box2d.coordPixelsToWorld(width,150);
chain.createChain(vertices, vertices.length);
Steps
Step 4: Attach the Shape to the body with a Fixture.
FixtureDef fd = new FixtureDef();
fd.shape = chain;
fd.density = 1;
fd.friction = 0.3;
fd.restitution = 0.5;
body.createFixture(fd);
Define a Surface
59
class Surface {
// We'll keep track of all of the surface points ArrayList<Vec2> surface;
Surface() {
surface = new ArrayList<Vec2>();
// Here we keep track of the screen coordinates of the chain surface.add(new Vec2(0, height/2));
//surface.add(new Vec2(width/2, height/2+50));
surface.add(new Vec2(width, height/2));
// This is what box2d uses to put the surface in its world ChainShape chain = new ChainShape();
// We can add 3 vertices by making an array of 3 Vec2 objects Vec2[] vertices = new Vec2[surface.size()];
for (int i = 0; i < vertices.length; i++) {
vertices[i] = box2d.coordPixelsToWorld(surface.get(i));
}
chain.createChain(vertices, vertices.length);
// The edge chain is now a body!
BodyDef bd = new BodyDef();
Body body = box2d.world.createBody(bd);
// Shortcut, we could define a fixture if we // want to specify frictions, restitution, etc.
body.createFixture(chain, 1);
}
How to draw it?
void display() { strokeWeight(1);
stroke(0);
fill(0);
beginShape();
for (int i=0; i<surface.size(); i++) { Vec2 v = surface.get(i);
vertex(v.x,v.y);
}
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
}
(0,height) (width,height)
vertex(v.x,v.y);
Exercise
Use the sin() to draw the mountains
Use the mouse to create circles
Use the CircleShape cs = new CircleShape() for body
61
Body type and parameters
float r = random(4,8);
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
FixtureDef fd = new FixtureDef();
fd.shape = cs;
fd.density = 1;
fd.friction = 0.01;
fd.restitution = 0.3;
. . .
body.setLinearVelocity(new Vec2(random(-10f,10f),random(5f,10f)));
Building Mountain:use the sin function
float angle = 0;
float angleVel = 0.2;
float amplitude = 100;
size(400,200);
background(255);
smooth();
stroke(0);
strokeWeight(2);
noFill();
beginShape();
for (int x = 0; x <= width; x += 5) {
//map one value from the current range to another range float y = map(sin(angle),-1,1,0,height);
vertex(x,y);
angle +=angleVel;
}
endShape();
63
Complex Shapes
How to create more complex shapes?
build a completely custom shape as a series of connected vertices
Vec2[] vertices = new Vec2[4];
// An array of 4 vectors
vertices[0] = box2d.vectorPixelsToWorld(new Vec2(-15, 25));
vertices[1] = box2d.vectorPixelsToWorld(new Vec2(15, 0));
vertices[2] = box2d.vectorPixelsToWorld(new Vec2(20, -15));
vertices[3] = box2d.vectorPixelsToWorld(new Vec2(-10, -10));
PolygonShape ps = new PolygonShape();
ps.set(vertices, vertices.length);
1. Order of vertices! If you are thinking in terms of pixels (as above) the vertices should be defined in counterclockwise order.
2. Convex shapes only! A concave shape is one where the surface curves inward. Convex is the opposite
65
Complex shapes
We can attach several shapes on a single body
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(center));
body = box2d.createBody(bd);
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
sd.setAsBox(box2dW, box2dH);
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
body.createFixture(ps,1.0);
body.createFixture(cs, 1.0);
Body center
Moving
67
Vec2 offset = new Vec2(0,-h/2);
//Converting the vector to Box2D world offset = box2d.vectorPixelsToWorld(offset);
//Setting the local position of the circle circle.m_p.set(offset.x,offset.y);
m_p : local center of shape
Setting shapes on body
Setting proper shapes on the body is really important for correct simulation.
If not, the collision detection does not work and simulation has errors.
Overlapping each other !!
Exercise : Revise the old example
69
void display() {
Vec2 pos = box2d.getBodyPixelCoord(body);
float a = body.getAngle();
rectMode(CENTER);
pushMatrix();
translate(pos.x,pos.y);
rotate(-a);
fill(175);
stroke(0);
//First the rectangle at (0,0) rect(0,0,w,h);
//Then the ellipse offset at (0,-h/2) ellipse(0,-h/2,r*2,r*2);
popMatrix();
}
Box2D Joints
Connecting one body to another
Three different joints
distance joints
revolute joints
mouse joints
Distant Joints
A joint that connects two bodies with a fixed length
Steps
Step 1. Make sure you have two bodies ready to go.
Step 2. Define the joint.
Step 3. Configure the joint’s properties (What are the bodies? Where are the anchors? What is its rest length? Is it elastic or rigid?)
Step 4. Create the joint.
71
Example
Assume that we have a particle body
Particle p1 = new Particle();
Particle p2 = new Particle();
DistanceJointDef djd = new DistanceJointDef();
djd.bodyA = p1.body;
djd.bodyB = p2.body;
//if our rest length is in pixels, we need to convert it!
djd.length = box2d.scalarPixelsToWorld(10);
Joint dj = (DistanceJoint) box2d.world.createJoint(djd);
Revolute Joints
connects two Box2D bodies at a common anchor point,
Steps
Step 1. Make sure you have two bodies ready to go.
Step 2. Define the joint.
Step 3. Configure the joint’s properties (What are the bodies?)
Step 4. Create the joint.
73
Example
Assume that we have a particle body
Box box1 = new Box();
Box box2 = new Box();
RevoluteJointDef rjd = new RevoluteJointDef();
//the anchor point (i.e. where they are connected)is set with the function initialize().
rjd.initialize(box1.body, box2.body, box1.body.getWorldCenter());
rjd.motorSpeed = PI*2; // how fast?
rjd.maxMotorTorque = 1000.0; // how powerful?
rjd.enableMotor = false; // is it on?
rjd.enableLimit = true;
rjd.lowerAngle = -PI/8;
rjd.upperAngle = PI/8;
Joint joint = box2d.world.createJoint(rjd);
Exercise: Let’s making a windmill
75
class Windmill { RevoluteJoint joint;
Box box1;
Box box2;
Windmill(float x, float y) {
// Initialize locations of two boxes
box1 = new Box(x, y-20, 120, 10, false);
box2 = new Box(x, y, 10, 40, true);
// Define joint as between two bodies
RevoluteJointDef rjd = new RevoluteJointDef();
rjd.initialize(box1.body, box2.body, box1.body.getWorldCenter());
// Turning on a motor (optional)
rjd.motorSpeed = PI*2; // how fast?
rjd.maxMotorTorque = 1000.0;
// how powerful?
rjd.enableMotor = false; // is it on?
// Create the joint rjd.enableLimit = true;
rjd.lowerAngle = -PI/8;
rjd.upperAngle = PI/8;
}
void display() { box2.display();
box1.display();
// Draw anchor just for debug
Vec2 anchor = box2d.coordWorldToPixels(box1.body.getWorldCenter());
fill(0);
noStroke();
ellipse(anchor.x, anchor.y, 8, 8);
} }
Create a ball whenever you click the mouse!
import pbox2d.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.contacts.*;
// A reference to our box2d world PBox2D box2d;
// An object to describe a Windmill (two bodies and one joint) Windmill windmill;
// An ArrayList of particles that will fall on the surface ArrayList<Particle> particles;
void setup() { size(800,200);
smooth();
// Initialize box2d physics and create the world box2d = new PBox2D(this);
box2d.createWorld();
// Make the windmill at an x,y location windmill = new Windmill(width/2,175);
// Create the empty list
particles = new ArrayList<Particle>();
}
void mousePressed() {
float sz = random(4,8); //radius
particles.add(new Particle(mouseX,mouseY,sz));
}
// Create a particle class!!
Class Particle {
//following the steps for creating body and shape }