Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Bouncing Ball 2 game, Game development, HTML5, Javascript and Phaser.

Earlier this year I published a tutorial series about Ketchapp‘s mobile hit Bouncing Ball, then I figured out there is a sequel of the game called Bouncing Ball 2 available for both iOS and Android.

The way to control the ball remains the same: a ball is bouncing on its own and the only way you have to control it is to increase its speed with a tap.

This time you don’t have to avoid spikes, you just have to land on randomly placed platforms. Have a look at the game:

Tap to start, then tap to boost ball speed and to land on platforms.

The game is very similar to the original Bouncing Ball with moving platforms rather than moving spikes, managed by Arcade physics and object pooling.

I made only a couple of major changes, mainly to prevent strange rebounds when the ball touches a platform close to its border:

1 – The ball is not actually a ball anymore. The sprite remains a circle, but the body is a square.

2 – The aforementioned square is smaller than the ball because we expect the ball to fall down if it lands on a platform on its very edge.

3 – We only check for collision on the bottom side of the ball

Have a look at the source code:

var game;
var gameOptions = {
    bounceHeight: 300,
    ballGravity: 1200,
    ballPower: 1200,
    ballPosition: 0.2,
    platformSpeed: 250,
    platformDistanceRange: [150, 250],
    platformHeightRange: [-50, 50],
    platformLengthRange: [40, 60],
    localStorageName: "bestballscore2"
}
window.onload = function() {
    let gameConfig = {
        type: Phaser.AUTO,
        backgroundColor:0x87ceeb,
        scale: {
            mode: Phaser.Scale.FIT,
            autoCenter: Phaser.Scale.CENTER_BOTH,
            parent: "thegame",
            width: 750,
            height: 500
        },
        physics: {
            default: "arcade",
            arcade:{
                debug: true
            }
        },
        scene: playGame
    }
    game = new Phaser.Game(gameConfig);
    window.focus();
}
class playGame extends Phaser.Scene{
    constructor(){
        super("PlayGame");
    }
    preload(){
        this.load.image("ground", "ground.png");
        this.load.image("ball", "ball.png");
    }
    create(){
        this.platformGroup = this.physics.add.group();
        this.firstBounce = 0;
        this.gameStart = false;
        this.ball = this.physics.add.sprite(game.config.width * gameOptions.ballPosition, game.config.height / 4 * 3 - gameOptions.bounceHeight, "ball");
        this.ball.body.gravity.y = gameOptions.ballGravity;
        this.ball.setBounce(1);
        this.ball.body.checkCollision.down = true;
        this.ball.body.checkCollision.up = false;
        this.ball.body.checkCollision.left = false;
        this.ball.body.checkCollision.right = false;
        this.ball.setSize(30, 50, true)
        let platformX = this.ball.x;
        for(let i = 0; i < 10; i++){
            let platform = this.platformGroup.create(platformX, game.config.height / 4 * 3 + Phaser.Math.Between(gameOptions.platformHeightRange[0], gameOptions.platformHeightRange[1]), "ground");
            platform.setOrigin(0.5, 1);
            platform.setImmovable(true);
            platform.displayWidth = Phaser.Math.Between(gameOptions.platformLengthRange[0], gameOptions.platformLengthRange[1]);
            platformX += Phaser.Math.Between(gameOptions.platformDistanceRange[0], gameOptions.platformDistanceRange[1]);
        }
        this.input.on("pointerdown", this.boost, this);
        this.score = 0;
        this.topScore = localStorage.getItem(gameOptions.localStorageName) == null ? 0 : localStorage.getItem(gameOptions.localStorageName);
        this.scoreText = this.add.text(10, 10, "");
        this.updateScore(this.score);
    }
    updateScore(inc){
        this.score += inc;
        this.scoreText.text = "Score: " + this.score + "\nBest: " + this.topScore;
    }
    boost(){
        if(this.firstBounce != 0){
            this.gameStart = true;
            this.ball.body.velocity.y = gameOptions.ballPower;
        }
    }
    getRightmostPlatform(){
        let rightmostPlatform = 0;
        this.platformGroup.getChildren().forEach(function(platform){
            rightmostPlatform = Math.max(rightmostPlatform, platform.x);
        });
        return rightmostPlatform;
    }
    update(){
        this.physics.world.collide(this.platformGroup, this.ball, function(){
            if(this.firstBounce == 0){
                this.firstBounce = this.ball.body.velocity.y;
            }
            else{
                this.ball.body.velocity.y = this.firstBounce;
                if(this.gameStart){
                    this.platformGroup.setVelocityX(-gameOptions.platformSpeed);
                }
            }
        }, null, this);
        this.platformGroup.getChildren().forEach(function(platform){
            if(platform.getBounds().right < 0){
                this.updateScore(1);
                platform.x = this.getRightmostPlatform() + Phaser.Math.Between(gameOptions.platformDistanceRange[0], gameOptions.platformDistanceRange[1]);
                platform.displayWidth = Phaser.Math.Between(gameOptions.platformLengthRange[0], gameOptions.platformLengthRange[1]);
            }
        }, this);
        if(this.ball.y > game.config.height){
            localStorage.setItem(gameOptions.localStorageName, Math.max(this.score, this.topScore));
            this.scene.start("PlayGame");
        }
    }
}

No matter if dealing with the first or the second installment of Bouncing Ball game, we always managed to create a working HTML5 prototype thanks to the power of Phaser. 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.