Skip to content
Stefan Hedman edited this page May 5, 2014 · 37 revisions

Contents

Welcome to the p2.js manual. This manual is supposed to cover the things that the automatically generated documentation don't. It should cover the majority of the p2.js API from the latest release (see releases).

It's assumed that you are familiar with basic physics concepts, such as mass, force, torque, and impulses. If not, consult Google and/or Wikipedia. p2.js is written in JavaScript, and hence you are also expected to be experienced in JavaScript programming.

If you have questions or feedback, please create a new issue.

Core concepts

Shape A geometrical shape, such as a sphere or a box.

Rigid body A piece of matter that is assumed indefinitely stiff. Any two points in a rigid body are assumed to always be at a constant distance from each other. We may refer to a rigid body by just saying "body". A rigid body has got a shape and a number of physical properties such as mass and inertia.

Constraint A constraint is a physical connection that removes degrees of freedom from bodies. In 3D a body has 6 degrees of freedom (three translation coordinates and three rotation coordinates). In 2D, there are 3 (two translational and one rotational). If we take a 3D door and put it on a door hinge we have constrained the door body to the wall. At this point the body can only rotate about the door hinge, so the constraint has removed 5 degrees of freedom.

Contact constraint A special constraint designed to prevent penetration of rigid bodies and to simulate friction and restitution. You do not create contact constraints; they are created automatically.

World A physics world is a collection of bodies and constraints that interact together.

Solver The physics world has a solver that is used to resolve constraints.

Units A unit is a way of measuring quantities such as length and time. In p2.js, we use meters-kilogram-second (MKS) units, and radians are used for angles. Do not use pixels for units.

Hello p2.js!

Let's create a simple physics world, with a dynamic circle on a static plane. We begin with creating the world.

var world = new p2.World({
    gravity:[0,-9.82]
});

This creates a World instance and sets gravity to 9.82 along the negative Y axis.

Now, let's create a circle body.

var circleBody = new p2.Body({
    mass:5,
    position:[0,10]
});

This will create an empty, 5-kilogram body at position x=0, y=10. To make it into a circle, we need to add a Shape to it.

var radius = 1;
var circleShape = new p2.Circle(radius);
circleBody.addShape(circleShape);

Now we have created a body and added a circle shape to it. If you start the simulation now, the circle will start falling and never stop. Let's create a plane that it can rest on.

var groundShape = new p2.Plane();
var groundBody = new p2.Body({
    mass:0
});
groundBody.addShape(groundShape);

By setting mass to zero, we tell the physics engine that the body should be static. By default, the position of a body is at the origin, which is fine for our Plane.

Before we can start our simulation, we must add our bodies to the world.

world.addBody(circleBody);
world.addBody(groundBody);

Now we are ready to integrate the world.

var timeStep = 1/60;
setInterval(function(){
    world.step(timeStep);
    console.log("Circle x position: " + circleBody.position[0]);
    console.log("Circle y position: " + circleBody.position[1]);
    console.log("Circle angle: " + circleBody.angle);
}, 1000*timeStep);

You will get the position and angle of the circle body printed to the console (no graphical output this time). The result might look something like this:

Circle x position: 0
Circle y position: 10
Circle angle: 0
Circle x position: 0
Circle y position: 9
Circle angle: 0
...
Circle x position: 0
Circle y position: 0.5
Circle angle: 0

Math

p2.js uses the glMatrix math library, with some extensions for vec2 and mat2. See the glMatrix documentation for full reference. The glMatrix math functions are exposed in the library, so you can use them like this:

var v = p2.vec2.create(),
    m = p2.mat2.create();

Collision

TODO

Dynamics

TODO

Shapes

The available shapes are Capsule, Circle, Convex, Line, Particle, Plane, and Rectangle.

Filtering shape collisions

You can use bit masks to enable or disable collisions between individual groups of shapes. For a full explanation, see this tutorial.

// Setup bits for each available group
var PLAYER = Math.pow(2,0),
    ENEMY =  Math.pow(2,1),
    GROUND = Math.pow(2,2)

// Put shapes into their groups
player1Shape.collisionGroup = PLAYER;
player2Shape.collisionGroup = PLAYER;
enemyShape  .collisionGroup = ENEMY;
groundShape .collisionGroup = GROUND;

// Assign groups that each shape collide with.
// Note that the players can collide with ground and enemies, but not with other players.
player1Shape.collisionMask = ENEMY | GROUND;
player2Shape.collisionMask = ENEMY | GROUND;
enemyShape  .collisionMask = PLAYER | GROUND;
groundShape .collisionMask = PLAYER | ENEMY;

This is how the collision filter check is done between two shapes:

if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
    // The shapes can collide
}

In JavaScript, you can use 32 valid groups (Math.pow(2,0) up to Math.pow(2,31)). However, group Math.pow(2,0) won't collide with any other shape group, and group Math.pow(2,31) will collide with all other groups.

Bodies

Motion states

The body can have one of three motion states, Body.STATIC, Body.KINEMATIC, and Body.DYNAMIC.

  • Dynamic bodies interact with all other bodies and can move.
  • Static bodies do not move, but interact with dynamic bodies.
  • Kinematic bodies can be controlled via velocity, but they behave like static bodies otherwise.

Mass properties

The body has mass and inertia. You can set the body mass when you create the body, or change it dynamically during simulation.

var body = new p2.Body({
  mass = 3
});
// Dynamically
body.mass = 1;
body.updateMassProperties();

Constraints

TODO

Equations

TODO

Events

Events fired during step

Some events are fired during the execution of world.step(). This is great, because it allows you to modify the behavior of the step. But you have to be careful: if you for example remove a body during a step then the world will most likely break apart. Therefore: read the documentation for the events you use, and be careful!

Don't do this:

world.on("beginContact",function(evt){
    world.remove(evt.bodyA); // BAD!
});

Instead, you can save the removal until after the step:

var removeBody;
world.on("beginContact",function(evt){
    removeBody = evt.bodyA;
});
// Simulation loop
setInterval(function(){
    world.step(timeStep);
    if(removeBody){
        world.remove(removeBody); // GOOD!
        removeBody = null;
    }
},1/60*1000);

Materials

TODO

World

Gravity

Gravity is global in the World, and it will be applied to all bodies each time step. You can set and get the current gravity vector from world.gravity.

world.gravity[0] = 0;     // x
world.gravity[1] = -9.82; // y
// or:
p2.vec2.set(world.gravity,0,-9.82);

There are times when you don't want to apply gravity to all bodies. In this case you must turn off global gravity and start applying gravity force yourself.

// Turn off global gravity
world.applyGravity=false;

// Keep track of which bodies you want to apply gravity on:
var gravityBodies=[body1,body2,body3];

// And just before running world.step(), do this:
var gravity = p2.vec2.fromValues(0,-9.82),
    gravityForce = p2.vec2.create();
for(var i=0; i<gravityBodies.length; i++){
    var b =  gravityBodies[i];
    p2.vec2.scale(gravityForce,gravity,b.mass); // F_gravity = m*g
    p2.vec2.add(b.force,b.force,gravityForce);  // F_body += F_gravity
}

Solvers

A solver is an algorithm for solving a linear systems of equations. In p2.js, it resolves constraints, contacts, and friction.

GSSolver

There are currently two solvers in p2.js. The most stable one is the GSSolver. This solver is iterative, which means that it converges to a solution in a number of iterations. In general, more iterations means better solution.

world.solver = new GSSolver();
world.solver.iterations =  5; // Fast, but contacts might look squishy...
world.solver.iterations = 50; // Slow, but contacts look good!

IslandSolver

Instead of solving the whole system at once, one can split it into independent parts (called "islands") and solve them independently. This has most benefit if the islands can be solved in parallel, but it still has some advantages when solving serially.

Solver parameters

The solver parameters are set on the Equation objects. You provide constraint stiffness and relaxation, like so:

equation.stiffness = 1e8;
equation.relaxation = 4;
equation.updateSpookParams(timeStep);

You can think of stiffness as the stiffness of a spring, which gives a force F=-k*x where x is the displacement of the spring. Relaxation is corresponds to the number of time steps you need to take to stabilize the constraint (larger value leads to a softer contact).

The mostly central equation types are ContactEquation and FrictionEquation. These equations are automatically created as contacts appear in your scene. To change the stiffness and relaxation of these, use the following ContactMaterial properties.

contactMaterial.stiffness = 1e8;
contactMaterial.relaxation = 3;
contactMaterial.frictionStiffness = 1e8;
contactMaterial.frictionRelaxation = 3;

You can also set stiffness and relaxation on Constraint equations. Just loop over all its equations one by one.

The Demo framework

TODO

Limitations

TODO

References

TODO

Clone this wiki locally