Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Box2D, HTML5, Javascript and Phaser.

I am a long time fan of Box2D physics engine, with almost 200 tutorials on this blog, most using old AS3, about 10 Flash games and five star book on Amazon.

Unfortunately Phaser 3 does not include any Box2D native wrapper as it does with Matter physics.

This does not mean we cannot use Box2D in our projects, it’s just we don’t have dedicated Phaser method to handle Box2D.

And, above all, we do not even have a native JavaScript version of Box2D.

There are some JavaScript ports of Box2D around the web, and the one I like the most is Planck.js by Ali Shakiba.

Planck.js is a JavaScript rewrite of Box2D physics engine while other JavaScript libraries are direct portings of the original C++ engine.

In this first example, something like a “Hello World”, I will show you these concepts:

* Create a Box2D world

* Add gravity

* Convert meters to pixels

* Add a body to the world

* Set a body as static or dynamic

* Add a rectangle fixture to a body

* Set body mass

* Set a body user data to store body graphic assets

* Update the simulation

* Iterate through bodies

* Adjust graphics assets according to the simulation

Like most physics engines, Box2D does not render graphics, it just does the math and physics work, and it’s up to you to update the canvas.

Have a look:

There’s no interactivity, just watch and remember good old Flash/Box2D days, which will probably have a little comeback because I am going to rewrite some old Box2D tutorials.

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);

        // createBox is a method I wrote to create a box, see how it works at line 55
        this.createBox(game.config.width / 2, game.config.height - 20, game.config.width, 40, false);

        // the rest of the script just creates a random box each 500ms, then restarts after 100 iterations
        this.tick = 0;
        this.time.addEvent({
            delay: 500,
            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);
    }

    update(){

        // advance the simulation by 1/20 seconds
        this.world.step(1 / 30);

        // crearForces  method should be added at the end on each step
        this.world.clearForces();

        // 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;
        }
    }
};

Probably it’s just me, but just declaring a worldScale variable makes me happy. Download the source code.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.