HTML5 game like “Bouncy Light” built with Phaser and Arcade physics – Step 2: adding falling platforms and commented source code

Read all posts about "" game

The HTML5 prototype of Bouncy Light game gets an update, and as promised we are going to add falling platforms and commented source code.

The fun part of these “jump over tiles” games lies in having different kind of tiles, each one with its own behaviour, and since it’s a very basic kind of game, the key of success when talking about tile types, is “the more, the better”.

We’ll start with a falling platform: it’s a platform that falls down once the player jumps on it, forcing the ball to reach another platform as quickly as possible. And what if running away from a falling platform you land on another falling platform?

Have a look at the game:

Just click/tap and hold to make ball move, release to stop it. Watch out for falling platforms.

The source code in step 1 was uncommented, there is nothing difficult in it anyway a commented source code is worth a thousand tutorials, and here it is:

let game;

// global game options object, to tune gameplay
let gameOptions = {

    // ground position, where 0: top of the screen, 1: bottom of the screen
    groundPosition: 3 / 4,

    // height of the bouncing ball, in pixel above ground position
    ballHeight: 300,

    // ball gravity used by ARCADE physics
    ballGravity: 2500,

    // ball x position, where 0: left of the screen, 1: righ of the screen
    ballPosition: 1 / 5,

    // platforms speed, in pixels per second
    platformSpeed: 950,

    // distance range from the center of each platform, in pixels
    platformDistanceRange: [150, 250],

    // height range of a platform, in pixels from ground position
    platformHeightRange: [-100, 100],

    // length range of a platform, in pixels
    platformLengthRange: [40, 60],

    // % of a platform to fall down after being hit
    fallingPlatformPercent: 50,

    // localStorage string name
    localStorageName: "bestballscore3"
}
window.onload = function() {

    // game configuration object
    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"
        },
        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(){

        // creation of the physics group which will contain all platforms
        this.platformGroup = this.physics.add.group();

        // ball sprite bound to an ARCADE body
        this.ball = this.physics.add.sprite(game.config.width * gameOptions.ballPosition, game.config.height * gameOptions.groundPosition - gameOptions.ballHeight, "ball");

        // set ball vertical gravity
        this.ball.body.gravity.y = gameOptions.ballGravity;

        // set maximum restitution to the ball
        this.ball.setBounce(1);

        // we will only check ball collision on its bottom side
        this.ball.body.checkCollision.down = true;
        this.ball.body.checkCollision.up = false;
        this.ball.body.checkCollision.left = false;
        this.ball.body.checkCollision.right = false;

        // make ball physics body a little narrower than its sprite
        this.ball.setSize(30, 50, true)

        // first platform will be exactly under the ball
        let platformX = this.ball.x;

        // we are going to create 10 platforms which we'll reuse to save resources
        for(let i = 0; i < 5; i++){

            // platform creation, as a member of platformGroup physics group
            let platform = this.platformGroup.create(0, 0, "ground");

            // platform won't physically react to collisions
            platform.setImmovable(true);

            // method to position the platform
            this.placePlatform(platform, platformX)

            // define next platform x position
            platformX += Phaser.Math.Between(gameOptions.platformDistanceRange[0], gameOptions.platformDistanceRange[1]);
        }

        // input management
        this.input.on("pointerdown", this.movePlatforms, this);
        this.input.on("pointerup", this.stopPlatforms, this);

        // score stats at zero
        this.score = 0;

        // retrieve top score from local storage, if any
        this.topScore = localStorage.getItem(gameOptions.localStorageName) == null ? 0 : localStorage.getItem(gameOptions.localStorageName);

        // text object to display the score
        this.scoreText = this.add.text(10, 10, "");

        // updateScore method will add its argument to current score and display it
        this.updateScore(0);
    }

    // method to add inc to current score and display it
    updateScore(inc){
        this.score += inc;
        this.scoreText.text = "Score: " + this.score + "\nBest: " + this.topScore;
    }

    // method to move platform to the left, called when the pointer is pressed
    movePlatforms(){
        this.platformGroup.setVelocityX(-gameOptions.platformSpeed);
    }

    // method to stop platforms, called when the pointer is released
    stopPlatforms(){
        this.platformGroup.setVelocityX(0);
    }

    // method to place "platform" platform at posX horizontal position
    placePlatform(platform, posX){

        // set platform x and y position
        platform.x = posX;
        platform.y = game.config.height * gameOptions.groundPosition + Phaser.Math.Between(gameOptions.platformHeightRange[0], gameOptions.platformHeightRange[1]);

        // platform will fall down if it's not the first platform AND if a random number between 1 and 100 is smaller than fallingPlatformPercent value
        platform.fallingDown = posX != this.ball.x && Phaser.Math.Between(1, 100) <= gameOptions.fallingPlatformPercent;

        // set platform display width
        platform.displayWidth = Phaser.Math.Between(gameOptions.platformLengthRange[0], gameOptions.platformLengthRange[1]);

        // (re)set platform body and velocity to zero
        platform.body.gravity.y = 0;
        platform.body.velocity.y = 0;
    }

    // method to get the rightmost platform, returns the position of the rightmost platform, in pixels
    getRightmostPlatform(){
        let rightmostPlatform = 0;
        this.platformGroup.getChildren().forEach(function(platform){
            rightmostPlatform = Math.max(rightmostPlatform, platform.x);
        });
        return rightmostPlatform;
    }

    // method to be executed at each frame
    update(){

        // handle collision between platform group and the ball
        this.physics.world.collide(this.platformGroup, this.ball, function(bodyA, bodyB){

            // if the platform is set to fall down, then assign it a high vertical gravity
            if(bodyB.fallingDown){
                bodyB.body.gravity.y = 1000
            }
        });

        // loop through all platforms
        this.platformGroup.getChildren().forEach(function(platform){

            // if a platform leaves the stage to the left side...
            if(platform.getBounds().right < 0){

                // increase and show the score
                this.updateScore(1);

                // reposition the platform
                this.placePlatform(platform, this.getRightmostPlatform() + Phaser.Math.Between(gameOptions.platformDistanceRange[0], gameOptions.platformDistanceRange[1]))
            }
        }, this);

        // if the ball falls down...
        if(this.ball.y > game.config.height){

            // save the best score
            localStorage.setItem(gameOptions.localStorageName, Math.max(this.score, this.topScore));

            // restart the scene
            this.scene.start("PlayGame");
        }
    }
}

There is more to do, such as adding more platform types and some eye candy effects such as particles, but at the moment that’s all, download the source code and enjoy.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

215 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stairs
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers