First Phaser 4 version released, let’s have a look at it by building a HTML5 Sokoban game

Read all posts about "" game

Here I am with a good news and a bad news. The good news is Phaser 4 has been released in a sort of beta version. The bad news is Phaser 4 is not ready for production at the moment, but being the good news positively GOOD, I don’t care about the bad news and I tried to develop something playable anywway.

There are some considerations to do: Phaser 4 is developed in TypeScript, and although we can continue developing in JavaScript, it’s recommended to switch to TypeScript, so during next Phaser tutorials I will show you how to switch from JavaScript to TypeScript. There’s nothing diffucult: you can check this guide I wrote 13 years ago about learning new languages. And bear in mind if you already know JavaScript, we can’t even consider TypeScript as a new language, let’s say it’s some kind of JavaScript on steroids.

When I said “not ready for production”, I meant “ready to start learning”, to get your knowledge grow as new and more stable versions are released.

This first example, is a Sokoban prototype built using my JavaScript Sokoban Class to handle game logic, so you will only need to focus on new Phaser features.

It’s not a pure TypeScript example, but rather a mix of JavaScript and Typescript, but it’s my “Hello World” so I will improve.

I used Parcel 2 to pack and bundle the game, if you are new to this kind of tool, don’t worry because next tutorial will focus on installing all necessary software on your machine.

Have a look at the game:

You know the rules, use arrow keys to move.

Have a look at the completely commented source code:

import { BackgroundColor, Parent, Scenes, WebGL, Size, GlobalVar } from '@phaserjs/phaser/config';
import { AddChild } from '@phaserjs/phaser/display/';
import { AddTween } from '@phaserjs/phaser/motion/tween/nano/AddTween';
import { Game } from '@phaserjs/phaser/Game';
import { Sprite } from '@phaserjs/phaser/gameobjects';
import { Scene } from '@phaserjs/phaser/scenes/Scene';
import { StaticWorld } from '@phaserjs/phaser/world/StaticWorld';
import { Keyboard } from '@phaserjs/phaser/input/keyboard';
import { LeftKey, RightKey, UpKey, DownKey } from '@phaserjs/phaser/input/keyboard/keys';
import { On } from '@phaserjs/phaser/events';
import { Sokoban } from './sokoban';
import * as tiles from './textures';

class SokobanGame extends Scene {

    // tile size, in pixels
    tileSize = 60;

    constructor () {
        super ();

        // initialize a new Phaser world
        const world = new StaticWorld (this);

        // array of static tiles to be placed.
        // these tiles never move
        const staticTiles = [
            tiles.floorTexture,     // when we have a floor tile
            tiles.wallTexture,      // when we have a wall tile
            tiles.goalTexture,      // when we have a goal tile
            tiles.floorTexture,     // when we have a crate tile, we draw the floor tile
            tiles.floorTexture,     // when we have the player tile, we draw the floor tile
            tiles.goalTexture,      // when we have a crate over goal tile, we draw the goal tile
            tiles.goalTexture       // when we have the player over goal tile, we draw the goal tile
        ]

        // add a keyboard input
        const keyboard = new Keyboard ();

        // define keys to be used in game
        this.leftKey = new LeftKey ();
        this.rightKey = new RightKey ();
        this.upKey = new UpKey ();
        this.downKey = new DownKey ();

        // add keyboard keys to check
        keyboard.addKeys (this.leftKey, this.rightKey, this.upKey, this.downKey);

        // player can move
        this.canMove = true;

        // array to store crates
        this.crates = [];

        // Sokoban level in standard text notation
        const levelString = '########\n#####@.#\n####.$$#\n#### $ #\n### .# #\n###    #\n###  ###\n########';

        // create a new Sokoban instance
        this.sokoban = new Sokoban ();

        // build the Sokoban level
        this.sokoban.buildLevelFromString (levelString);

        // iterate through Sokoban level
        this.sokoban.level.map (function (row, rowNumber) {

            // iterate through Sokoban level row
            row.map (function (element, columnNumber) {

                // create a new sprite using the proper tile according to element
                const tile = new Sprite(this.tileSize * (columnNumber + 0.5), this.tileSize * (rowNumber + 0.5), staticTiles[element]);

                // add the tile to the world
                AddChild (world, tile);

                // handle player and crates
                switch (element) {

                    // player
                    case 4:
                    case 6:

                        // create player sprite
                        this.player =  new Sprite (this.tileSize * (columnNumber + 0.5), this.tileSize * (rowNumber + 0.5), tiles.playerTexture);
                        break;

                    // crate
                    case 3:
                    case 5:

                        // create crate sprite and add to crates array
                        this.crates.push (new Sprite (this.tileSize * (columnNumber + 0.5), this.tileSize * (rowNumber + 0.5), tiles.crateTexture));
                }
            }.bind (this));
        }.bind (this));

        // add player sprite to the world
        AddChild (world, this.player);

        // add all crate sprites to the world
        this.crates.map (function (crate) {
            AddChild (world, crate);
        })

        // method to call at each frame
        On(this, 'update', () => this.update ());
    }

    // method called at each frame
    update () {

        // can the player move?
        if (this.canMove) {

            // check if the move is legal
            let move = this.leftKey.isDown ? this.sokoban.moveLeft () : (this.rightKey.isDown ? this.sokoban.moveRight () : (this.upKey.isDown ? this.sokoban.moveUp () : (this.downKey.isDown ? this.sokoban.moveDown () : false)))

            // is the move legal?
            if (move) {

                // now we can't move
                this.canMove = false;

                // retrieve plater information
                let player = this.sokoban.getPlayer();

                // retrieve crates information
                let crates = this.sokoban.getCrates();

                // move the player
                this.player.x += (player.getColumn () - player.getPrevColumn ()) * this.tileSize;
                this.player.y += (player.getRow () - player.getPrevRow ()) * this.tileSize;

                // iterate through all crates
                crates.map (function (crate, index) {

                    // did this crate move?
                    if (crate.hasMoved ()) {

                        // move crate sprite accordingly
                        this.crates[index].x += (crates[index].getColumn () - crates[index].getPrevColumn ()) * this.tileSize;
                        this.crates[index].y += (crates[index].getRow () - crates[index].getPrevRow ()) * this.tileSize;
                    }
                }.bind (this))
            }
        }

        // can't the player move?
        else {

            // if we aren't pressing any arrow key...
            if (!this.downKey.isDown && !this.upKey.isDown && !this.leftKey.isDown && !this.rightKey.isDown) {

                // now we can move again
                this.canMove = true;
            }
        }
    }
}

new Game(
    WebGL(),                // renderer
    GlobalVar('Phaser4'),   // global variables
    Parent('thegame'),      // <div> element where to render
    Size(480, 480, 1),      // game size
    Scenes(SokobanGame)     // game scenes
);

And this is the content of textures.js:

import { PixelTexture } from '@phaserjs/phaser/textures/types';
import { PICO8 } from '@phaserjs/phaser/textures/palettes';

export const floorTexture = PixelTexture({
    data: [
        '66666',
        '66666',
        '66666',
        '66666',
        '66666'
    ],
    pixelWidth: 12,
    pixelHeight: 12,
    palette: PICO8
});

export const wallTexture = PixelTexture({
    data: [
        '11111',
        '11111',
        '11111',
        '11111',
        '11111'
    ],
    pixelWidth: 12,
    pixelHeight: 12,
    palette: PICO8
});

export const goalTexture = PixelTexture({
    data: [
        '66666',
        '66866',
        '68886',
        '66866',
        '66666'
    ],
    pixelWidth: 12,
    pixelHeight: 12,
    palette: PICO8
});

export const playerTexture = PixelTexture({
    data: [
        '.CCC.',
        '.FFF.',
        'CC.CC',
        '.CCC.',
        '.C.C.'
    ],
    pixelWidth: 12,
    pixelHeight: 12,
    palette: PICO8
});

export const crateTexture = PixelTexture({
    data: [
        '55555',
        '54.45',
        '5...5',
        '54.45',
        '55555'
    ],
    pixelWidth: 12,
    pixelHeight: 12,
    palette: PICO8
});

It’s not that much, but it’s the first Phaser 4 working game you can find in the web. I will add swipe control and animations as soon as I’ll figure out how to handle some callbacks, meanwhile download the source code, Sokoban class included.

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