Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Pocket Snap game, Game development, HTML5, Javascript and Phaser.

When playing with the first step of Pocket Snap prototype, you surely missed a target where to try to make balls land.

The original game features some “U” shaped targets, which remainds a bit “Trick Shot” tutorial series, which was built using Phaser 2 and Box2D, and will get updated soon with the new Plank.js library.

Meanwhile, let’s stick to Matter physics and see what I did with it:

Tap and hold to charge, release to fire the ball.

Try to make the ball land into the target.

The game restarts a couple of seconds after you fired the ball with some random parameters.

If you managed to hit the target, you will see a “Yeah” message.

At the bottom of the “U” shape there is a sensor. Sensors work just like normal bodies triggering collisions, but they are ethereal.

Look at the 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,
        physics: {
            default: "matter",
            matter: {
                gravity: {
                    y: 1
                },
                debug: true
            }
        }
    }
    game = new Phaser.Game(gameConfig);
    window.focus();
}
class playGame extends Phaser.Scene{
    constructor(){
        super("PlayGame");
    }
    create(){

        // matter settings
        this.matter.world.update30Hz();
        this.matter.world.setBounds(10, 10, game.config.width - 20, game.config.height - 20);

        // random cannon properties
        let angle = Phaser.Math.Between(60, 75);
        let width = Phaser.Math.Between(20, 30);
        let length = Phaser.Math.Between(120, 250);
        let tickness = Phaser.Math.Between(10, 12);
        let position = new Phaser.Math.Vector2(game.config.width / 6, 550);

        // bottom body
        let bottomWidth = width + tickness * 2;
        let bottomBody = this.matter.add.rectangle(position.x, position.y, bottomWidth, tickness, this.setProperties(true, angle));

        // some trigonometry useful to find the origins of cannon side bodies
        let bottomCathetus = (width + tickness) / 2;
        let sideCathetus = (length + tickness) / 2;
        let hypotenuse = Math.sqrt(Math.pow(bottomCathetus, 2) + Math.pow(sideCathetus, 2));
        let bottomAngle = Phaser.Math.RadToDeg(Math.asin(sideCathetus / hypotenuse));

        // side body 1
        let firstSideOrigin = this.moveBy(position, hypotenuse, 90 - bottomAngle - angle)
        this.matter.add.rectangle(firstSideOrigin.x, firstSideOrigin.y, tickness, length, this.setProperties(true, angle));

        // side body 2
        let secondSideOrigin = this.moveBy(position, hypotenuse, bottomAngle - 90 - angle)
        this.matter.add.rectangle(secondSideOrigin.x, secondSideOrigin.y, tickness, length, this.setProperties(true, angle));

        // trigger
        let triggerOrigin = this.moveBy(position, (length - (width * 3 - tickness) / 2), -angle)
        let trigger = this.matter.add.rectangle(triggerOrigin.x, triggerOrigin.y, width, width, this.setProperties(false, angle));

        // cannon ball
        let ballOrigin = this.moveBy(position, (width + length - (width * 3 - tickness) / 2), -angle)
        this.matter.add.circle(ballOrigin.x, ballOrigin.y, width / 2, this.setProperties(false, angle, "ball"));

        // constraint
        let constraintLength = length + (tickness - width * 3) / 2;
        this.constraint = this.matter.add.constraint(bottomBody, trigger, constraintLength, 1);
        this.constraintFireLength = constraintLength + width;
        this.constrainMinLength = width / 2 + tickness / 2;

        // random target properties
        let targetWidth = Phaser.Math.Between(80, 120);
        let targetHeight = Phaser.Math.Between(120, 180);
        let targetTickness = Phaser.Math.Between(10, 20);
        let targetPosition = new Phaser.Math.Vector2(Phaser.Math.Between(game.config.width / 4 * 3, game.config.width / 5 * 4), game.config.height - Phaser.Math.Between(40, 80));

        // target
        this.matter.add.rectangle(targetPosition.x, targetPosition.y, targetWidth + targetTickness * 2, targetTickness, {
            isStatic: true
        });
        this.matter.add.rectangle(targetPosition.x + (targetWidth + targetTickness) / 2, targetPosition.y - (targetHeight + targetTickness) / 2, targetTickness, targetHeight, {
            isStatic: true
        });
        this.matter.add.rectangle(targetPosition.x - (targetWidth + targetTickness) / 2, targetPosition.y - (targetHeight + targetTickness) / 2, targetTickness, targetHeight, {
            isStatic: true
        });

        // this is the sensor used
        this.matter.add.rectangle(targetPosition.x, targetPosition.y - targetTickness, targetWidth, targetTickness, {
            isStatic: true,
            isSensor: true,
            label: "goal"
        });

        // a text to show if the player hits the target
        this.yeahText = this.add.text(game.config.width / 2, 200, "YEAH !!", {
            fontFamily: "Arial",
            fontSize: 64,
            color: "#00ff00"
        })
        this.yeahText.setOrigin(0.5);
        this.yeahText.setVisible(false);

        // check for collision between the sensor and the ball
        this.matter.world.on("collisionstart", function(event, bodyA, bodyB){
            if((bodyA.label == "ball" && bodyB.label == "goal") || (bodyA.label == "goal" && bodyB.label == "ball")){

                // show the text if the ball hits the sensor
                this.yeahText.visible = true;
            }
        }, this);

        // listeners and flags
        this.input.on("pointerdown", this.charge, this);
        this.input.on("pointerup", this.fire, this);
        this.charging = false
    }

    // charge
    charge(){
        this.charging = true;
    }

    // fire: look how stiffness changes, then restart the game
    fire(){
        this.charging = false;
        this.constraint.stiffness = 0.02
        this.constraint.length = this.constraintFireLength;
        this.time.addEvent({
            delay: 3500,
            callbackScope: this,
            callback: function(){
                this.scene.start("PlayGame");
            },
        });
    }

    // we reduce constraint length if charging
    update(){
        if(this.charging && this.constraint.length > this.constrainMinLength){
            this.constraint.length -= 1;
        }
    }

    // utility method to create an object with body properties
    setProperties(isStatic, angle, label){
        if(label == undefined){
            label  = "";
        }
        let radians = Phaser.Math.DegToRad(90 - angle);
        return {
            isStatic: isStatic,
            angle: radians,
            friction: 0,
            label: label,
            restitution: 0.4
        }
    }

    // utility method to move a point by "distance" pixels in "degrees" direction
    moveBy(point, distance, degrees){
        let radians = Phaser.Math.DegToRad(degrees);
        return new Phaser.Math.Vector2(point.x + distance * Math.cos(radians), point.y + distance * Math.sin(radians));
    }
};

We managed to create targets and react when the player fires the ball into the hole, next time we’ll see how to move targets, meanwhile 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.