Build a HTML5 game like the great ZX Spectrum game “Yanga” using Phaser – Step 1

Read all posts about "" game

If you love videogames, you don’t care which platform they are developed for. You just play great games, everywhere.

So I am introducing you Yanga, a game made by Serdjuk for the ZX Spectrum. Yes, the ZX Spectrum, the 8-bit personal home computer released in 1982, 39 years ago.

You should definitively play it, as it’s an interesting mix between Sokoban, a match 3 game and an action puzzle game.

This short GIF video represents level 1.

I am showing you how to draw a Yanga level, starting from a Tiled map array filled with objects. In this case, just like with Totem Destroyer prototype, I preferred to use objects rather than tiles because they allow more room for customization.

This is level 2:

And this is the Tiled scheme of the level:

As you can see, I did not draw walls tile by tile, but with bigger objects. Basically because I am lazy.

And this is my version of the level:

There is no interaction or input management at the moment, but there are the glowing runes and walls, both player and exit have their animations, and wall tiles are randomly placed, rotated, flipped and colored.

The orginal game has not been decompiled. You should never decompile games, the real challenge is the rebuild it on your own. I used the original graphics just to let you see how the prototype looks like the original game.

Now, the completely commented source code:

let game;

let gameOptions = {

    // tile size in Tiled map
    tiledSize: 32,

    // tile size in the game
    actualSize: 64,

    // wall colors
    wallColors: [0xcecb00, 0x00fb00, 0x00cbce],

    // rune colors
    runeColors: [0xff0000, 0x0000ff],

    // player color
    playerColor: 0xcecbce,

    // door colors
    doorColors: [0xff0000, 0x00fb00]
}

window.onload = function() {
    let gameConfig = {
        type: Phaser.AUTO,
        scale: {
            mode: Phaser.Scale.FIT,
            autoCenter: Phaser.Scale.CENTER_BOTH,
            parent: "thegame",
            width: 960,
            height: 640
        },
        scene: playGame
    }
    game = new Phaser.Game(gameConfig);
    window.focus();
}

class playGame extends Phaser.Scene {

    constructor() {
        super("PlayGame");
    }

    preload() {

        // load tiled map
        this.load.tilemapTiledJSON("level", "level.json");

        // player sprites
        this.load.spritesheet("player", "player.png", {
            frameWidth: gameOptions.actualSize,
            frameHeight: gameOptions.actualSize
        });

        // door sprites
        this.load.spritesheet("door", "door.png", {
            frameWidth: gameOptions.actualSize,
            frameHeight: gameOptions.actualSize
        });

        // wall tiles
        this.load.spritesheet("tiles", "tiles.png", {
            frameWidth: gameOptions.actualSize,
            frameHeight: gameOptions.actualSize
        });
    }

    create() {

        // add the tilemap
        let map = this.add.tilemap("level");

        // array to store runes
        this.runeWallArray = [];

        // player animation
        this.anims.create({
            key: "playerAnim",
            frames: this.anims.generateFrameNumbers("player", {
                frames: [0, 1, 0, 2]
            }),
            frameRate: 8,
            repeat: -1
        });

        // door animation
        this.anims.create({
            key: "doorAnim",
            frames: this.anims.generateFrameNumbers("door", {
                frames: [0, 2, 1, 3, 1, 2, 0]
            }),
            frameRate: 8,
            repeat: -1
        });

        // select all objects in Object Layer zero, the first - and only, at the moment - level
        let blocks = map.objects[0].objects;

        // looping through all blocks and execute addBlock method
        blocks.forEach (blocks => this.addBlock(blocks));

        // give some kind of glow effect to runes
        this.tweens.add({
            targets: this.runeWallArray,
            alpha: 0.8,
            duration: 100,
            yoyo: true,
            repeat: -1
        });
    }

    // method to add a level block
    addBlock(block) {

        // get block x and y position
        let blockX = block.x / gameOptions.tiledSize * gameOptions.actualSize + gameOptions.actualSize / 2;
        let blockY = block.y / gameOptions.tiledSize * gameOptions.actualSize + gameOptions.actualSize / 2;

        // check block type
        switch (block.type) {

            // wall
            case "Wall":

                // get wall width and height
                let wallWidth = block.width / gameOptions.tiledSize;
                let wallHeight = block.height / gameOptions.tiledSize;

                // place the tiles
                for (let i = 0; i < wallWidth; i ++) {
                    for (let j = 0; j < wallHeight; j ++) {

                        // place tile sprite
                        let tile = this.add.sprite(blockX + i * gameOptions.actualSize, blockY + j * gameOptions.actualSize, "tiles", Phaser.Math.RND.integerInRange(0, 3));

                        // give randomness to tile
                        tile.setFrame(Phaser.Math.RND.integerInRange(0, 3));
                        tile.angle = Phaser.Math.RND.integerInRange(0, 3) * 90;
                        tile.flipX = Phaser.Math.RND.integerInRange(0, 1) == 1;
                        tile.flipY = Phaser.Math.RND.integerInRange(0, 1) == 1;
                        tile.setTint(Phaser.Math.RND.pick(gameOptions.wallColors));
                    }
                }
                break;

            // rune
            case "Rune":
                let runeValue = block.properties[0].value;
                let rune = this.add.sprite(blockX, blockY, "tiles", 4 + 2 * runeValue);
                rune.setTint(gameOptions.runeColors[runeValue])
                break;

            // rune wall
            case "Rune Wall":
                let wallValue = block.properties[0].value;
                let runeWall = this.add.sprite(blockX, blockY, "tiles", 5 + 2 * wallValue);
                runeWall.setTint(gameOptions.runeColors[wallValue]);
                this.runeWallArray.push(runeWall);
                break;

            // player
            case "Player":
                let player = this.add.sprite(blockX, blockY, "player");
                player.play("playerAnim");
                player.setTint(gameOptions.playerColor);
                break;

            // entrance door
            case "Entrance":
                let entrance = this.add.sprite(blockX, blockY, "door");
                entrance.setTint(gameOptions.doorColors[0]);
                break;

            // exit door
            case "Exit":
                let exit = this.add.sprite(blockX, blockY, "door");
                exit.setTint(gameOptions.doorColors[1]);
                exit.play("doorAnim");
                break;
        }
    }
}

Next time I am going to add game logic and input management, then you can start building your Yanga-like game. Download the source code, Tiled project included.

And don’t forget to have a look at the original game.

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

214 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
// 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