Build a HTML5 game like “Color Jump” using Phaser 3 and Matter physics – adding coins and commenting the source code

Read all posts about "" game

As promised, here we go with the second step of Color Jump prototype.

In the first step we saw the basic prototype of the game, and now it’s time to add collectible coins, just like we did in the fourth part of “Don’t Touch the Spikes” prototype.

Look at the game:

Tap to jump. Hit the walls matching the color of the ball, or it’s game over. If you fall down, it’s game over. If you fly off the top, it’s game over. Try to collect the coins.

Exactly like in Don’t Touch the Spikes, coins introduce a new kind of Matter body called sensor. Sensors trigger collision events, but do not react with colliding body physically, so the player won’t bounce on the coin, and it’s exactly what we need.

And now you have the full commented source code, line by line:

// the game itself
let game;

// global game options
let gameOptions = {

    // world gravity
    gravity: 4,

    // ball horizontal speed
    ballSpeed: 4,

    // jump force
    jumpForce: 30,

    // amount of bars each wall is divided in
    bars: 4,

    // array with the colors to pick from
    barColors: [0x1abc9c, 0x2980b9, 0x9b59b6, 0xf1c40f, 0xc0392b, 0xecf0f1]
}

// constants used to pass "LEFT" and "RIGHT" as arguments rather than "0" and "1"
const LEFT = 0;
const RIGHT = 1;

// function to be executed when the windows has loaded
window.onload = function() {

    // object containing configuration options
    let gameConfig = {

        // render type: let the game decide if CANVAS of WEBGL
        type: Phaser.AUTO,

        // width of the game, in pixels
        width: 750,

        // height of the game, in pixels
        height: 1334,

        // background color (black)
        backgroundColor: 0x000000,

        // scene to play
        scene: playGame,

        // physics settings
        physics: {

            // we are using Matter JS
            default: "matter",
            matter: {

                // gravity settings
                gravity: {
                    x: 0,
                    y: gameOptions.gravity
                }
            }
        }
    }

    // game creation
    game = new Phaser.Game(gameConfig);

    // giving focus to the frame (if any) where the game is running in
    window.focus();

    // pure javascript to scale the canvas
    resize();
    window.addEventListener("resize", resize, false);
}

// playGame scene
class playGame extends Phaser.Scene{
    constructor(){
        super("PlayGame");
    }

    // preloading assets
    preload(){
        this.load.image("wall", "wall.png");
        this.load.image("ball", "ball.png");
        this.load.image("coin", "coin.png");
    }

    // method to be executed once, when the scene has been created
    create(){

        // arrays where to store left and right walls
        this.leftWalls = [];
        this.rightWalls = [];

        // each wall is made by "gameOptions.bars" pieces, so we actually have "gameOptions.bars" walls each side
        for(let i = 0; i < gameOptions.bars; i++){

            // adding left and right walls
            this.leftWalls[i] = this.addWall(i, LEFT);
            this.rightWalls[i] = this.addWall(i, RIGHT);
        }

        // adding the ball
        this.ball = this.matter.add.image(game.config.width / 4, game.config.height / 2, "ball");

        // setting ball body as circular
        this.ball.setCircle()

        // adding the coin, no matter where, we'll set its position later
        this.coin = this.matter.add.image(0, 0, "coin");

        // setting coin body as circular
        this.coin.setCircle();

        // setting coin body as static (not affected by gravity or collisions)
        this.coin.setStatic(true);

        // setting coin body as sensor. Will fire collision events without actually collide
        this.coin.body.isSensor = true;

        // giving the coin a label called "coin"
        this.coin.body.label = "coin"

        // this method will randomly place the coin
        this.placeCoin();

        // setting ball velocity (horizontal, vertical)
        this.ball.setVelocity(gameOptions.ballSpeed, 0);

        // waiting for pointer down input to call "jump" method
        this.input.on("pointerdown", this.jump, this);

        // waiting for a "collisionstart" event. "e" is the event, "b1" and "b2" the bodies
        this.matter.world.on("collisionstart", function (e, b1, b2) {

            // checking b1 and b2 labels to be "leftwall"
            if(b1.label == "leftwall" || b2.label == "leftwall"){

                // handling collisions on the LEFT side
                this.handleWallCollision(LEFT, b1, b2);
            }

            // checking b1 and b2 labels to be "rightwall"
            if(b1.label == "rightwall" || b2.label == "rightwall"){

                // handling collisions on the RIGHT side
                this.handleWallCollision(RIGHT, b1, b2);
            }

            // checking b1 and b2 labels to be "coin"
            if(b1.label == "coin" || b2.label == "coin"){

                // calling the method to move the coin elsewhere
                this.placeCoin();
            }
        }, this);
    }

    // method to add a wall, given its number (0 = top) and it side
    addWall(wallNumber, side){

        // getting "wall" preloaded image
        let wallTexture = this.textures.get("wall");

        // determining wall height according to game height and the number of bars
        let wallHeight = game.config.height / gameOptions.bars;

        // determining wall x position
        let wallX = side * game.config.width + wallTexture.source[0].width / 2 - wallTexture.source[0].width * side;

        // determining wall y position
        let wallY = wallHeight * wallNumber + wallHeight / 2;

        // adding the wall
        let wall = this.matter.add.image(wallX, wallY, "wall");

        // the wall is static
        wall.setStatic(true);

        // giving the wall the proper label
        wall.body.label = (side == RIGHT) ? "rightwall" : "leftwall"

        // setting wall height
        wall.displayHeight = wallHeight;

        // returning the wall object
        return wall
    }

    // method to place the coin
    placeCoin(){

        // just placing the coin in a random position between 20% and 80% of the game size
        this.coin.x = Phaser.Math.Between(game.config.width * 0.2, game.config.width * 0.8);
        this.coin.y = Phaser.Math.Between(game.config.height * 0.2, game.config.height * 0.8);
    }

    // method to handle ball Vs wall collision
    handleWallCollision(side, bodyA, bodyB){

        // if the ball and the wall have different colors...
        if(bodyA.color != bodyB.color){

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

        // calling a method to paint the walls
        this.paintWalls((side == LEFT) ? this.rightWalls : this.leftWalls);

        // updating ball velocity
        this.ball.setVelocity(gameOptions.ballSpeed, this.ball.body.velocity.y);
    }

    // method to paint the walls, in the argument the array of walls
    paintWalls(walls){

        // looping through all walls
        walls.forEach(function(wall){

            // picking a random color
            let color = Phaser.Math.RND.pick(gameOptions.barColors);

            // tinting the wall
            wall.setTint(color);

            // also assigning the wall body a custom "color" property
            wall.body.color = color;
        });

        // picking a random wall
        let randomWall = Phaser.Math.RND.pick(walls);

        // painting the ball with the same color used by the random wall
        this.ball.setTint(randomWall.body.color);

        // also assigning the ball body a custom "color" property
        this.ball.body.color = randomWall.body.color;
    }

    // method to jump
    jump(){

        // setting new ball velocity
        this.ball.setVelocity((this.ball.body.velocity.x > 0) ? gameOptions.ballSpeed : -gameOptions.ballSpeed, -gameOptions.jumpForce);
    }

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

        // updating ball velocity
        this.ball.setVelocity((this.ball.body.velocity.x > 0) ? gameOptions.ballSpeed : -gameOptions.ballSpeed, this.ball.body.velocity.y);

        // if the ball flies off the screen...
        if(this.ball.y < 0 || this.ball.y > game.config.height){

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

// pure javascript to resize the canvas and scale the game
function resize(){
    let canvas = document.querySelector("canvas");
    let windowWidth = window.innerWidth;
    let windowHeight = window.innerHeight;
    let windowRatio = windowWidth / windowHeight;
    let gameRatio = game.config.width / game.config.height;
    if(windowRatio < gameRatio){
        canvas.style.width = windowWidth + "px";
        canvas.style.height = (windowWidth / gameRatio) + "px";
    }
    else{
        canvas.style.width = (windowHeight * gameRatio) + "px";
        canvas.style.height = windowHeight + "px";
    }
}

What about some particle effects next time? I will show you how to add eye-candy effects. Download the source code.

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