Talking about Box2D, HTML5, Javascript and Phaser.
Now that Flash is dead, a lot of physics games powered by Box2D are
destined for oblivion.
I still consider Box2D the most powerful physics engine to build simple physics games, and I know what I am talking about since I wrote almost 200 tutorials on this blog and I published a book which has been rated with five stars on Amazon (don’t buy it, as it covers Box2D for Flash, I might consider to rewrite it to be used with Phaser).
The best Box2D JavaScript port, in my opinion, is Planck.js, which also features Space, an interactive playground to build, test and modify your projects.
I already showed you an example a long time ago, but it’s definitively time to write more tutorials about it, to bring cute physics games to a new life.
Here we go with an example using a revolute joint to simulate a bar.
A revolute joint forces two bodies to share a common anchor point, often called a hinge point. The revolute joint has a single degree of freedom: the relative rotation of the two bodies. This is called the joint angle.
Look at this example:
There is no interactivity, but random boxes are created each 1.5 seconds and fall over two bodies with a revolute joint and a motor.
The result is a moving platform like in my old Stabilize! prototype which would be interesting to build again using these modern libraries.
Have a look at the completely commented source code:
let game;
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 600,
height: 600
},
scene: playGame
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class playGame extends Phaser.Scene {
constructor() {
super("PlayGame");
}
create() {
// Box2D works with meters. We need to convert meters to pixels.
// let's say 30 pixels = 1 meter.
this.worldScale = 30;
// world gravity, as a Vec2 object. It's just a x, y vector
let gravity = planck.Vec2(0, 3);
// this is how we create a Box2D world
this.world = planck.World(gravity);
// creation of the pivot body and the bar body with a custom method, "createBox", explained at line
let pivot = this.createBox(game.config.width / 2, game.config.height / 5 * 4, 10, 10, false);
let bar = this.createBox(game.config.width / 2, game.config.height / 5 * 4, game.config.width * 0.8, 20, true);
// a revolute joint
this.revoluteJoint = this.world.createJoint(planck.RevoluteJoint({
// first body
bodyA: pivot,
// second body
bodyB: bar,
// bodies acnhor point
anchorPoint: bar.getWorldCenter(),
// max motor torque force
maxMotorTorque: 120,
// motor speed
motorSpeed: 0,
// the motor is enabled
enableMotor: true
}));
// the rest of the script just creates a random box each 1500ms, then restarts after 100 iterations
this.tick = 0;
this.time.addEvent({
delay: 1500,
callbackScope: this,
callback: function() {
this.createBox(Phaser.Math.Between(100, game.config.width - 100), -100, Phaser.Math.Between(20, 80), Phaser.Math.Between(20, 80), true);
this.tick ++;
if (this.tick == 100) {
this.scene.start("PlayGame");
}
},
loop: true
});
}
// here we go with some Box2D stuff
// arguments: x, y coordinates of the center, with and height of the box, in pixels
// we'll conver pixels to meters inside the method
createBox(posX, posY, width, height, isDynamic) {
// this is how we create a generic Box2D body
let box = this.world.createBody();
if (isDynamic) {
// Box2D bodies born as static bodies, but we can make them dynamic
box.setDynamic();
}
// a body can have one or more fixtures. This is how we create a box fixture inside a body
box.createFixture(planck.Box(width / 2 / this.worldScale, height / 2 / this.worldScale));
// now we place the body in the world
box.setPosition(planck.Vec2(posX / this.worldScale, posY / this.worldScale));
// time to set mass information
box.setMassData({
mass: 1,
center: planck.Vec2(),
// I have to say I do not know the meaning of this "I", but if you set it to zero, bodies won't rotate
I: 1
});
// now we create a graphics object representing the body
var color = new Phaser.Display.Color();
color.random();
color.brighten(50).saturate(100);
let userData = this.add.graphics();
userData.fillStyle(color.color, 1);
userData.fillRect(- width / 2, - height / 2, width, height);
// a body can have anything in its user data, normally it's used to store its sprite
box.setUserData(userData);
return box;
}
update(t, dt) {
// advance world simulation
this.world.step(dt / 1000 * 2);
// crearForces method should be added at the end on each step
this.world.clearForces();
// adjust joint motor speed according to its angle
this.revoluteJoint.setMotorSpeed(this.revoluteJoint.getJointAngle() * -0.1)
// iterate through all bodies
for (let b = this.world.getBodyList(); b; b = b.getNext()) {
// get body position
let bodyPosition = b.getPosition();
// get body angle, in radians
let bodyAngle = b.getAngle();
// get body user data, the graphics object
let userData = b.getUserData();
// adjust graphic object position and rotation
userData.x = bodyPosition.x * this.worldScale;
userData.y = bodyPosition.y * this.worldScale;
userData.rotation = bodyAngle;
}
}
};
We can create old physics games using Phaser and Planck, are you interested in more tutorials about it? Download the source code and give me feedback.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.