Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Castle Ramble game, Game development, HTML5, Javascript and Phaser.

Castle Ramble by Chorrus Games, available for iOS and Android, is an infinite roguelite platformer where you can shoot at tiles and break them.

What a nice idea! Why not building a HTML5 prototype to let the hero break tiles?

We’ll start from the Yeah Bunny prototype, split the screen in two, then you will be able to jump by tapping on the left side of the screen and fire by tapping on the right side of the screen.

Once a bullet hits a tile, BOOM, the tile no longer exists. Have a look:

Click or tap on the left half of the screen to jump, double jump or wall jump. You can also slide on walls. By clicking or tapping on the right side of the screen, you will fire a bullet which can break walls.

This adds a great twist to tile based games, and here you can find the completely commented source code of the prototype:

let game;
let gameOptions = {

    // player gravity
    playerGravity: 900,

    // player friction when on wall
    playerGrip: 100,

    // player horizontal speed, in pixels per second
    playerSpeed: 200,

    // player jump force
    playerJump: 400,

    // player double jump force
    playerDoubleJump: 300,

    // bullet speed, in pixels per second
    bulletSpeed: 400
}

// constant to make "destructible" more readable
const DESTRUCTIBLE = 1;
window.onload = function() {
    let gameConfig = {
        type: Phaser.AUTO,
        backgroundColor: 0x444444,
        scale: {
            mode: Phaser.Scale.FIT,
            autoCenter: Phaser.Scale.CENTER_BOTH,
            parent: "thegame",
            width: 640,
            height: 480
        },
        physics: {
            default: "arcade",
            arcade: {
                gravity: {
                    y: 0
                }
            }
        },
        scene: [preloadGame, playGame]
    }
    game = new Phaser.Game(gameConfig);
    window.focus();
}
class preloadGame extends Phaser.Scene{
    constructor(){
        super("PreloadGame");
    }
    preload(){
        this.load.tilemapTiledJSON("level", "level.json");
        this.load.image("tile", "tile.png");
        this.load.image("hero", "hero.png");
        this.load.image("bullet", "bullet.png");
    }
    create(){
        this.scene.start("PlayGame");
    }
}
class playGame extends Phaser.Scene{
    constructor(){
        super("PlayGame");
    }
    create(){

        // creation of "level" tilemap
        this.map = this.make.tilemap({
            key: "level"
        });

        // adding tiles to tilemap
        let tile = this.map.addTilesetImage("tileset01", "tile");

        // tiles 1 and 2 have collision enabled
        this.map.setCollision([1, 2]);

        // which layer should we render? "layer01"
        this.layer = this.map.createDynamicLayer("layer01", tile);

        // add the hero sprite and enable ARCADE physics
        this.hero = this.physics.add.sprite(300, 376, "hero");

        // set hero horizontal speed
        this.hero.body.velocity.x = gameOptions.playerSpeed;

        // the hero can jump
        this.canJump = true;

        // the hero cannot double jump
        this.canDoubleJump = false;

        // the hero is not on the wall
        this.onWall = false;

        // the hero can fire a bullet
        this.canFire = true;

        // add the bullet and enable arcade physics
        this.bullet = this.physics.add.sprite(-20, -20, "bullet");

        // wait for player input
        this.input.on("pointerdown", this.handleInput, this);

        // set workd bounds to allow camera to follow the player
        this.cameras.main.setBounds(0, 0, 1920, 1440);

        // make the camera follow the player
        this.cameras.main.startFollow(this.hero);
    }

    handleInput(e){

        // left half of the screen = jump
        if(e.x < game.config.width / 2){

            // the hero can jump when:
            // canJump is true AND the hero is on the ground (blocked.down)
            // OR
            // the hero is on the wall
            if((this.canJump && this.hero.body.blocked.down) || this.onWall){

                // apply jump force
                this.hero.body.velocity.y = -gameOptions.playerJump;

                // is the hero on a wall?
                if(this.onWall){

                    // change the horizontal velocity too. This way the hero will jump off the wall
                    this.setPlayerXVelocity(true);
                }

                // hero can't jump anymore
                this.canJump = false;

                // hero is not on the wall anymore
                this.onWall = false;

                // hero can now double jump
                this.canDoubleJump = true;
            }
            else{

                // can the hero make the doubple jump?
                if(this.canDoubleJump){

                    // the hero can't double jump anymore
                    this.canDoubleJump = false;

                    // apply double jump force
                    this.hero.body.velocity.y = -gameOptions.playerDoubleJump;
                }
            }
        }

        // right half of the sceen = fire
        else{

            // can the player fire?
            if(this.canFire){

                // player can't fire anymore
                this.canFire = false;

                // place the bullet at player position
                this.bullet.x = this.hero.x;
                this.bullet.y = this.hero.y;

                // set bullet speed
                this.bullet.body.velocity.x = gameOptions.bulletSpeed * ((this.hero.body.velocity.x > 0) ? 1 : -1);
            }
        }
    }
    update(){

        // set some default gravity values. Look at setDefaultValues method for more information
        this.setDefaultValues();

        // handle collision between bullet and tiles
        this.physics.world.collide(this.bullet, this.layer, function(bullet, layer){

            // stop the bullet
            bullet.body.velocity.x = 0;

            // place the bullet out of the stage
            bullet.x = -20;

            // now the player can fire again
            this.canFire = true;

            // is the tile destructible?
            if(layer.index == DESTRUCTIBLE){

                // remove the tile from the map
                this.map.removeTileAt(layer.x, layer.y);
            }
        }, null, this);

        // handle collision between hero and tiles
        this.physics.world.collide(this.hero, this.layer, function(hero, layer){

            // some temporary variables to determine if the player is blocked only once
            let blockedDown = hero.body.blocked.down;
            let blockedLeft = hero.body.blocked.left
            let blockedRight = hero.body.blocked.right;

            // if the hero hits something, no double jump is allowed
            this.canDoubleJump = false;

            // hero on the ground
            if(blockedDown){

                // hero can jump
                this.canJump = true;
            }

            // hero on the ground and touching a wall on the right
            if(blockedRight){

                // horizontal flipping hero sprite
                hero.flipX = true;
            }

            // hero on the ground and touching a wall on the right
            if(blockedLeft){

                // default orientation of hero sprite
                hero.flipX = false;
            }

            // hero NOT on the ground and touching a wall
            if((blockedRight || blockedLeft) && !blockedDown){

                // hero on a wall
                hero.scene.onWall = true;

                // remove gravity
                hero.body.gravity.y = 0;

                // setting new y velocity
                hero.body.velocity.y = gameOptions.playerGrip;
            }

            // adjust hero speed according to the direction it's moving
            this.setPlayerXVelocity(!this.onWall || blockedDown);
        }, null, this)
    }

    // default values to be set at the beginning of each update cycle,
    // which may be changed according to what happens into "collide" callback function
    // (if called)
    setDefaultValues(){
        this.hero.body.gravity.y = gameOptions.playerGravity;
        this.onWall = false;
        this.setPlayerXVelocity(true);
    }

    // set player velocity according to the direction it's facing, unless "defaultDirection"
    // is false, in this case multiplies the velocity by -1
    setPlayerXVelocity(defaultDirection){
        this.hero.body.velocity.x = gameOptions.playerSpeed * (this.hero.flipX ? -1 : 1) * (defaultDirection ? 1 : -1);
    }
}

Another great game concept built in a few lines thanks to Phaser power. Download the source code and play with it.

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