(click anywhere to close)
OPEN MENU

[Phaser] Physics P2 System

category: Games | course: Phaser | difficulty:

The p2 system implemented in Phaser, is the most powerful and broad one. It allows for very realistic collisions, has some useful extras (like chains, constraints, springs, thrust, etc.), and all of this without it being too hard to understand. However, due to the great size of this system, I will not cover all of it, just the fundamentals.

Initiating the P2 system

This isthe syntax for initiating the system, and below that the syntax for enabling physics to be used on a certain sprite.

// adding P2 physics to the game
game.physics.startSystem(Phaser.Physics.P2JS);

// Enabling a body (=making physics available) on a certain sprite
game.physics.p2.enable(sprite1);

Creating different bodies

Now that you know this, you want to create the bodies for your game. By default P2 will simply create a rectangle around your sprite, but that's not always what you want. You can tell P2 to draw a certain shape as a body for your sprite like this:

//All shapes are added to the anchor point of the body, which is automatically set to (0.5,0.5) = the exact center.

//Add a capsule shape (you know, a mix between an ellipse and a (rounded) rectangle)
someSprite.body.addCapsule(length, radius, offsetX, offsetY, rotation);

//Add a circle
someSprite.body.addCircle(radius, offsetX, offsetY, rotation);

//Add a line (running from [-length/2,0] to [length/2, 0])
someSprite.body.addLine(length, offSetX, offsetY, rotation);

//Add a rectangle
someSprite.body.addRectangle(width, height, offsetX, offsetY, rotation);

//Add a polygon
someSprite.body.addPolygon(options, points);

/*'options' is an object (optionally) asking for three things:
optimalDecomp (boolean): whether or not to optimally calculate physics (increases CPU load)
skipSimpleCheck (boolean): set to true if you want to skip checking if the polygon intersects itself
removeCollinearPoints (boolean): set to true if you want to remove them
		collinear points is the middle point when three points lie in a straight line, and is basically useless

*/

/*'points' is an array containing arrays with x,y values, like this:
[[0,2],[2,3],[4,1],[6,6]]

and it of course represents the points/vertices/corners of this polygon
*/

//The function for clearing all shapes from the body, use it when you want to add a single different body than the default rectangle that's added to it
//Because, you might have noticed, every method above 'adds' to the body.
someSprite.body.clearShapes();

Useful body attributes

Now that you've initiated the system and enabled a body with the correct shape(s) on your sprite, you want to do stuff with them (I think, I can't read your mind).

//Set damping (friction), velocity and force
someSprite.body.damping, someSprite.body.force, someSprite.body.velocity
//This can also be applied to object's rotation, simply: 
angularDamping, angularVelocity, angularForce

//But, you can also tell it to just never rotate:
someSprite.body.fixedRotation = true;

//Set the gravity
someSprite.body.gravity = someValue;

//Make the body collide with the world bounds
someSprite.body.collideWorldBounds = true;

//Set the mass
someSprite.body.mass = someValue;

//set the angle (same as rotation, but set in radians, which is more efficient)
someSprite.body.angle = someValue;

//Allow sleep on a body
//Sleepingstate is turned on when a body is not/barely (see sleepSpeedLimit) doing anything. This helps save resources, because a lot of checks and functions don't need to be called by the system
someSprite.body.allowSleep();
someSprite.body.sleepSpeedLimit = someValue;

//Make a body kinematic or static (default is dynamic)
//Kinematic: general physics don't apply (e.g. gravity), but it still can collide
someSprite.body.kinematic = true;

//Static: the body just cannot move, but everything else can bump into it
someSprite.body.static = true;

Knowing that, you can do pretty much everything. But, to simplify matters, there's a few standard functions:

//Moving backward, forward, left, right, up, down
moveBackward(speed), moveForward(speed)
moveLeft(speed), moveRight(speed)
moveUp(speed), moveUp(down)

//Rotate left or right
rotateLeft(speed), rotateRight(speed)

//Thrust and reverse (move forward or backward in the direction you're looking at)
thrust(speed);
reverse(speed);

Mighty Methods and Easy Events

In P2, there's a lot of events you can listen for. Most of them keep track of when things are added/removed or when something begins/ends. Therefore, unlike the other systems, there are no 'global' collide and overlap functions available where you give the function two objects and they collide. This time, this functionality exists on a body itself. But, another thing p2 handles differently, is the fact that you can only collide objects with one or multiple collision groups.This means that everything you want the physics world to interact with, must be part of a certain collision group. This might seem unnecessary some times, but this helps speed up p2 and might even help you organize your game/code. So, basic collision detection goes like this:

//A script for colliding a ship (our player) with rocks
var ship;

function create() {
    //  Enable P2
    game.physics.startSystem(Phaser.Physics.P2JS);

    //  Turn on impact events for the world, without this we get no collision callbacks
    game.physics.p2.setImpactEvents(true);

    //  Create our collision groups. One for the player, one for the rocks
    var playerCollisionGroup = game.physics.p2.createCollisionGroup();
    var rockCollisionGroup = game.physics.p2.createCollisionGroup();

    //Create rocks at random positions
    var rocks = game.add.group();
    rocks.enableBody = true;
    rocks.physicsBodyType = Phaser.Physics.P2JS;

    for (var i = 0; i < 8; i++)
    {
        var rock = rocks.create(game.world.randomX, game.world.randomY, 'rock');
        //  Tell the rock to use the rockCollisionGroup 
        rock.body.setCollisionGroup(rockCollisionGroup);

        //  Make the rocks collide with other rocks, and the player
        rock.body.collides([rockCollisionGroup, playerCollisionGroup]);
    }

    //  Create our ship sprite (give it a circle as collision body, don't allow it to rotate
    ship = game.add.sprite(200, 200, 'ship');
    game.physics.p2.enable(ship, false);
    ship.body.setCircle(28);
    ship.body.fixedRotation = true;

    //  Set the ships collision group
    ship.body.setCollisionGroup(playerCollisionGroup);

    // When our player/ship hits one of the rocks, a function is fired (hitrock)
    // And if it happens, reduce the rock's alpha value
    ship.body.collides(rockCollisionGroup, hitrock, this);

}

function hitrock(body1, body2) {

    //  body1 is the body that owns the callback (the ship)
    //  body2 is the body it impacted with (our rock)
    //  As body2 is a Phaser.Physics.P2.Body object, you access its owner (the sprite) via the sprite property:
    body2.sprite.alpha *= 0.9;

}

You might think that's an awful lot of code for such a simple thing. But, I've included some extras here to give you a better idea of how this works, because it's very important to understand that.

Now that you know this, you might have noticed that the possibility to check if two things overlap (which is equally important as the collide() function) isn't discussed. Well, that's because that is hidden between the Phaser Signals / Event Listeners you can call on the P2 system.

This is discussed more thoroughly in another post, but a short summary: Phaser has built in methods that create so called 'phaser signals' when a certain event happens. All you have to do then, is pick up these signals and hand them a function to execute.

//Basic syntax for listening for events
//The context parameter is optional, and determines what defines the word 'this'
game.physics.p2.<someEvent>.add(functionToCall, context);

//These are all the possible events to listen for
onBeginContact, onEndContact
onBodyAdded, onBodyRemoved,
onContactMaterialAdded, onContactMaterialRemoved
onConstraintAdded, onConstraintRemoved,
onSpringAdded, onSpringRemoved

However, although I said that those simple 'collide'/'overlap' functions don't exist here, there is an alternative that can be used sometimes, that checks if there's any body under a certain point in the world (most common use: checking all the bodies under your mouse pointer).

game.physics.p2.hitTest(worldPoint, bodies, precision, skipStatic);
//Worldpoint: the point (x,y) in this world we'll check against
//Bodies (optional, array): a list of bodies to check against, if not set, it checks for all the bodies in the world
//Precision (optional, number): for checking very small objects (like lines)
//skipStatic (optional,boolean): whether or not to include static bodies when hitTesting

//This function returns an array of bodies that hit that point in the world. To access the sprites attached to them, use .parent.sprite
 

Extra possibilities

Next to almost pixel-perfect collision, p2 offers some extra features to help speed up your game making process.

ContactMaterials

ContactMaterials are what p2 uses for calculating what happens when two materials collide. With the system, you can create several materials, and assign them to objects. Then, you can say 'hey, if this material and that one collide, I want this to happen'. An implementation:

function create() {

    //  Enable p2 physics
    game.physics.startSystem(Phaser.Physics.P2JS);

    //Set overall world gravity
    game.physics.p2.gravity.y = 300;

    //  Add a sprite
    var player = game.add.sprite(200, 200, 'dude');
    game.physics.p2.enable(player);
    player.body.fixedRotation = true;

    //Create a material for the player (second paramater tells the game to automatically assign this material to the player's body)
    var spriteMaterial = game.physics.p2.createMaterial('spriteMaterial', player.body);
    //Create another material
    var worldMaterial = game.physics.p2.createMaterial('worldMaterial');

    // And assign this one to the world (4 trues for left/right/top/bottom faces)
    game.physics.p2.setWorldMaterial(worldMaterial, true, true, true, true);

    //Create a contact material and set it to the two materials we created earlier
    // Whenever the player touches the world's edges, the following happens
    var contactMaterial = game.physics.p2.createContactMaterial(spriteMaterial, worldMaterial);

    contactMaterial.friction = 0.0;     // Friction to use in the contact of these two materials.
    contactMaterial.restitution = 0.0;  // Restitution (i.e. how bouncy it is!) to use in the contact of these two materials.
    contactMaterial.stiffness = 1e7;    // Stiffness of the resulting ContactEquation that this ContactMaterial generate.
    contactMaterial.relaxation = 0;     // Relaxation of the resulting ContactEquation that this ContactMaterial generate.
    contactMaterial.frictionStiffness = 1e7;    // Stiffness of the resulting FrictionEquation that this ContactMaterial generate.
    contactMaterial.frictionRelaxation = 3;     // Relaxation of the resulting FrictionEquation that this ContactMaterial generate.
    contactMaterial.surfaceVelocity = 0.0;        // Will add surface velocity to this material. If bodyA rests on top if bodyB, and the surface velocity is positive, bodyA will slide to the right.

}
Springs

Adding a spring, is as easy as coding gets:

//  The parameters are: createSpring(sprite1, sprite2, restLength, stiffness, damping)
var spring = game.physics.p2.createSpring(sprite1, sprite2, 150, 5, 1);
Constraints

A constraint basically defines a relation between two bodies: either a fixed distance, limits to their movement or rotation, fixing them to a certain point(/body) in space, reverse rotation between them (for gears for example), etc. This can't be done with one constraint function, therefore there are multiple ones covering all of the above: LockConstraint,DistanceConstraint, GearConstraint, PrismaticConstraint, RevoluteConstraint.

LockConstraint

This is the most simple one: simply puts a fixed x and y offset between two bodies. It's almost the same as adding a body as a child of another body, so it moves in the exact same way, but the advantage of the lockconstraint is that you can keep the seperate bodies and easily specify distance.

game.physics.p2.createLockConstraint(bodyA, bodyB, offset, angle, maxForce);
//BodyA and BodyB = the two bodies being constraint
//offset: The offset between 'em
//angle: The angle between 'em
//maxForce (optional): the maximum force the physics system is allowed to use when pulling them together. Too low: constraining takes too long, too high: you get some supernatural speeds in your game
DistanceConstraint

A distanceConstraint keeps two bodies at the same distance from each other, but allows all other types of movement. This means that for example one body can circle around the other, staying at the same distance the whole time.

game.physics.p2.createDistanceConstraint(bodyA, bodyB, distance, maxForce);
GearConstraint

A gear constraint doesn't constrain distance, but rotation/angle. This type sets the relative rotation between two bodies (so, if one body would rotate 180 degrees, and the relative rotation would be 0.5, the other one would rotate 90 degrees).

game.physics.p2.createGearConstraint(bodyA, bodyB, relativeAngle);
RevoluteConstraint

A revolute constraint is a more difficult one to understand. It connects two bodies at given points, and let's them rotate relative to each other around this point. Sounds complicated, but you can better think of these as a hinge or pivot joint. One body is the hinge itself of which the center represents the pivot point of the second body. The second body then has all the physics applied, but can only rotate around this fixed point.

game.physics.p2.createRevoluteConstraint(bodyA, pivotA, bodyB, pivotB, maxForce);
//bodyA & bodyB = the two bodies being constrained
//pivotA, pivotB -> the point relative to the center of the body which it is constrained to
     //In case of a hinge, the first body could be a circle with it's pivot point at its center (so pivotA = (0,0))
     //The second one is the body (let's say rectangle) to rotate around that spot, which has it's pivotB set to the upper corner ( (-width/2, -height))
Prismatic Constraint

This one's even harder to understand (well, at least I thought it was difficult). Basically, a prismatic constraint forbids bodies any movement along one axis, and makes it able to freely translate along the other.

game.physics.p2.createPrismaticConstraint(bodyA, bodyB, lockRotation, anchorA, anchorB, axis, maxForce)
//bodyA, bodyB = the two bodies being constrained
//NOTE: the rest of the parameters is completely optional
//lockRotation (boolean) = whether or not the bodies are allowed to rotate
//anchorA, anchorB = the bodies' anchor point (set relative to its own local frame)
Limits to constraints

If you've managed to figure out the type of constraint you want, and you applied it perfectly, you sometimes want to limit their behavior. This goes the same for all types, like this:

someConstraint.upperLimit = someValue;
someConstraint.lowerLimit = someValue;

//You can add a motor to constraints:
//Set the speed
someConstraint.setMotorSpeed(12);

//Enable it: this makes the motor do its job
someConstraint.enableMotor() 

//Disable it: motor falls quiet, object follows gravity and all other basic physics rules again
someConstraint.disableMotor()
Chains

P2 doesn't have an easy function for creating chains, but using some revoluteConstraints we can easily simulate them. And as seen that chains are something you often need in physics games, I'll elaborate a bit on it! (the code below is for a big part taken from Phaser's Examples page, and only a bit modified to make it easier to understand (I hope))

function preload() {
    //Load a spritesheet here
    //Two frames: first with one link of the chain from the front, second with one link of the chain from the side
    game.load.spritesheet('chain','./assets/sprites/chain.png',16,26);
}


function create() {
   //Start physics system, apply world gravity
    game.physics.startSystem(Phaser.Physics.P2JS);
    game.physics.p2.gravity.y = 1200;

    //Call the createRope function we're going to create below
    createRope(40,400,100);  // (length, xAnchor, yAnchor)
};



function createRope(length, xAnchor,yAnchor){
    //Variable containing the last link, so we can fix the new one to it
    var lastRect;
    //Width and height of the physics body (height is less than the actual image, to make it look better)
    var height = 20;
    var width = 16;
    //The force that holds this chain together
    var maxForce =20000;
    //Create as many links as the length variable demands
    for(var i=0; i<=length; i++){
        //Set x and y anchor: the rope is straight down, so x doesn't change, y does with 'height' amount
        var x = xAnchor;
        var y = yAnchor+(i*height);
        //Switch between frame 0 and 1 (and bring the last created piece on top, for prettier visuals)
        if (i%2==0) {
            newRect = game.add.sprite(x, y, 'chain',1)
        } else {
            newRect = game.add.sprite(x, y, 'chain',0); 
            lastRect.bringToTop();
        } 
        //Enable a body on the new rectangle
        game.physics.p2.enable(newRect,false);
        //Set a custom rectangle for the body (for same reason that height is less than the actual image)
        newRect.body.setRectangle(width,height);

        //If this piece is the first one, make it static => anchor it to the xAnchor and yAnchor provided.
        if (i==0) {
            newRect.body.static=true; 
        } else {  
            //If not, give the piece a push (just to show you the effect of the rope)
            newRect.body.velocity.x = 100;
            //Reduce mass for every element (why? because the lower you get, the less gravitational energy is applied)
            newRect.body.mass =  length/i;
        }
        //From our second element on, we want to create a constraint between this one and the last one
        if(lastRect) {
            //Top of the new rectangle (0,-10), locks in with the bottom of the last rectangle (0,10)
            game.physics.p2.createRevoluteConstraint(newRect, [0,-10], lastRect, [0,10], maxForce);
        }
        //Update the last rectangle created!
        lastRect = newRect;
    }; 
}
   
CONTINUE WITH THIS COURSE
Do you like my tutorials?
To keep this site running, donate some motivational food!
Crisps
(€2.00)
Chocolate Milk
(€3.50)
Pizza
(€5.00)