Make Cars and Trucks in Phaser

4/16/17

This tutorial shows how to make cars and trucks using Phaser P2 Physics. We will cover constraining wheels to the truck body, increasing wheel grip, and creating a bouncy chassis effect that is seen in many popular games today. You will gain the skills to create your own 2D driving game with custom mechanics! See what we will be creating here.

The Game: Bouncy Truck

The game that we will design simply demonstrates the functionality of a truck that can scale inclines and bounce. Download the source code from Github here. Alternatively, you can use this command on your command line in your preferred file directory, assuming you have Git installed:

git clone https://github.com/Loonride/bouncy-truck.git

Serve the source code using a web server and ensure that the game is working by using arrow keys and the space bar.

Physics Bodies

First, take a look at the asset folder of the source. We will need polygon physics bodies for the truck and the hill. We don't need this for the wheel because we can just give it a circle with P2. Drop truck.png and hill.png in Loon Physics. Get the hill to look like this:

Get the truck to look like this with 15-20 vertices:

Now click JSON File, or just use the file that we provided and skip this step.

Preload

Look at game.js in a text editor. In the preload state we load our images and physics data:

game.load.image('truck', 'asset/truck.png');
game.load.image('wheel', 'asset/wheel.png');
game.load.image('hill', 'asset/hill.png');
game.load.physics("physics", "asset/physics.json");

Game World & P2

Let's move on to the create state. Now we will initialize the game world. We will set a large boundary width so that the truck has room to drive around. We will also start the P2 physics engine and set world gravity.

game.world.setBounds(0,0,width*2,height);
game.physics.startSystem(Phaser.Physics.P2JS);
game.physics.p2.gravity.y = 300;

Truck

It's time to create our truck. We need to add the sprite, enable P2, and enable the physics polygon that we created earlier:

truck = game.add.sprite(width*0.25, height*0.8, "truck");
game.physics.p2.enable(truck, showBodies);
truck.body.clearShapes();
truck.body.loadPolygon("physics", "truck");

Earlier we made our world bound width twice the size of the viewport width. We can easily ensure that we will see the truck by following it with the camera:

game.camera.follow(truck);

Wheels

What would a truck be without wheels? Let's first create a wheel group:

wheels = game.add.group();

Before adding the wheels, we need to create materials for the wheels and the world so that we can define what happens when they come in contact. We will get back to this later.

wheelMaterial = game.physics.p2.createMaterial("wheelMaterial");
var worldMaterial = game.physics.p2.createMaterial("worldMaterial");

Now we can add the wheels. We will make a function called initWheel that adds a wheel at the desired position. It will take a coordinate array in the form [x,y], relative to the truck. It will look like this in use:

var distBelowTruck = 24;
initWheel([55,distBelowTruck]);
initWheel([-52, distBelowTruck]);

So far we have followed the code sequentially, but let's jump down to initWheel to see exactly what it does. The function skeleton looks like this:

function initWheel(offsetFromTruck) {
    ...
}

We first position the wheels relative to the position of the truck:

var truckX = truck.position.x;
var truckY = truck.position.y;
var wheel = game.add.sprite(truckX + offsetFromTruck[0],
		truckY + offsetFromTruck[1], "wheel");

Positioning them will not simply hold them there, but we will fix that in a moment. Let's add a circular physics polygon to the wheel:

game.physics.p2.enable(wheel, showBodies);
wheel.body.clearShapes();
wheel.body.addCircle(15.5);

Now comes the important part. We need the wheels to hold on to the truck at a pivot point, where they can rotate freely. Take a look at the code that does this:

var maxForce = 100;
var rev = game.physics.p2.createRevoluteConstraint(truck.body, offsetFromTruck,
	wheel.body, [0,0], maxForce);

As said in the P2 docs, a revolute constraint "Connects two bodies at given offset points, letting them rotate relative to each other around this point." The createRevoluteConstraint parameters look like this:

createRevoluteConstraint(bodyA, pivotA, bodyB, pivotB, maxForce)

So the pivot point for the truck is the place that we want to place the wheel. The pivot point of the wheel is simply the center of the wheel, so [0,0]. maxForce is an optional parameter, and it is the maximum force that will be exerted to keep the pivots of the two bodies at the same point in the world. If not specified, it will be the equivalent of infinite force. We set a maximum force of 100 to give the chassis of the truck a bouncy effect. Try making the number larger or smaller in your code!

We should add the wheel to the wheels group:

wheels.add(wheel);

Now we create an event listener to see when the wheel comes in contact with something.

We will get back to the function that it calls later.

wheel.body.onBeginContact.add(onWheelContact, game);

Finally, we set the wheel's material. We created this material earlier.

wheel.body.setMaterial(wheelMaterial);

Contact Materials

Let's jump back to the create state after we called initWheel. First we need to set the world boundary material for all of the walls:

game.physics.p2.setWorldMaterial(worldMaterial, true, true, true, true);

Now the wheels and the ground have materials

A contact material in Phaser allows us to define the friction and restitution (bounciness) between two materials when they are in contact. Lets create our contact material between the world and the wheels:

var contactMaterial = game.physics.p2.createContactMaterial(wheelMaterial,worldMaterial);

We have to do this because the default friction between the ground and the wheels is too low for the wheels to have any grip. Let's change that by applying a large friction for the contact material, and let's also apply a small restitution (bounciness):

contactMaterial.friction = 1e3;
contactMaterial.restitution = .3;

See what happens when you change these values!

Controls and Bouncing

We should initialize arrow keys and the space bar first:

cursors = game.input.keyboard.createCursorKeys();
var spaceKey = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

Now we should apply an upward force to the truck body when the space key is pressed, making it bounce!

spaceKey.onDown.add(function() {
	if (allowTruckBounce) {
		truck.body.moveUp(500);
		allowTruckBounce = false;
	}
}, game);

The truck should only bounce if it has touched the ground since it last bounced. Earlier, we created a listener for when the wheels come in contact with something, which calls onWheelContact. Let's take a look at that function.

Wheel Contact

When onWheelContact is called, many arguments of body contact information are sent, two of which we use:

function onWheelContact(phaserBody, p2Body) {
    ...
}

Within the function, we use these arguments to figure out if the wheel came in contact with the ground or the hill. If it did come in contact with one of these two things, we allow another truck bounce.

if ((phaserBody === null && p2Body.id == 4)
|| (phaserBody && phaserBody.sprite.key == "hill")) {
	allowTruckBounce = true;
}

Driving

Finally, we need to spin the wheels with our arrow keys! In the update method, we rotate all the wheels left, right, or set zero rotation based on the arrow keys that are down:

var rotationSpeed = 300;
if (cursors.left.isDown) {
	wheels.children.forEach(function(wheel,index) {
		wheel.body.rotateLeft(rotationSpeed);
	});
}
else if (cursors.right.isDown) {
	wheels.children.forEach(function(wheel,index) {
		wheel.body.rotateRight(rotationSpeed);
	});
}
else {
	wheels.children.forEach(function(wheel,index) {
		wheel.body.setZeroRotation();
	});
}

Conclusion

We did not go over adding the hill, but it is simple compared to all the other concepts we covered! Be sure to read the comments in the source code if you are confused by something. We hope that you now have the skills to create your own driving game! Do you want to see more P2 truck tutorials? Let us know!

Until next time,

Loonride

Up Next

Subscribe

  • No spam
  • Update information
  • Free newsletters

Invalid Email

Subscribed

Try Again Later