HTML5 prototype of mobile hit “Flipping Legend” with top down view made with Phaser

Read all posts about "" game
Did you play Flipping Legend by Hiding Spot Games and NoodleCake Studio? From the 5 stars TouchArcade review: “Flipping Legend has you playing as one of several character types who have the ability to flip diagonally across the world, much like a bishop on a chess board. So, you can’t hit anything right in front of you, necessarily. You have to be constantly thinking in diagonals, trying to string together jumps, because your health constantly decreases when you’re not bopping enemies”. We are going to try to build something similar using HTML5 with Phaser. We’ll need to scale down the gane to a two-dimensional game, loosing some visual appeal but allowing to be played in portrait mode, which is an interesting feature. As all infinite runners, there’s nothing “infinite” and the player won’t move, but it’s the entire game to move towards the player, giving the idea of the player running through an infinite world. Also, we need two player sprites, as player can disappear from one side of the path appearing on the opposite side of the path, so in this case the “I am moving out” and the “I am moving in” effects are made each one with its own sprite and tween. Last but not least, tweens and tiles are reused and once the game starts there’s no need to create new assets. Let’s have a look at the game:
Click or tap on the left/right half of the canvas to move the hero accordingly. If you have a mobile device, play it directly from this link. And this is the completely commentd source code, pay attention at the way I am using two sprites when the hero wraps around the path, and how I manage multiple resolutions:
// the game itself
var game;

// global object with game options
var gameOptions = {

    // width of the game, in pixels
    gameWidth: 640,

    // tint colors to be applied to tiles
    tileColors: [0x00ff00, 0x00aa00],

    // number of tiles visible, works better if it's even, in this first prototype
    verticalTiles: 9
}
window.onload = function() {

    // determining window width and height
    var windowWidth = window.innerWidth;
    var windowHeight = window.innerHeight;

    // if we are in ladscape mode, then set window height to fake a potrait mode
    if(windowWidth > windowHeight){
        windowHeight = windowWidth * 1.8;
    }

    // defining game height
    var gameHeight = windowHeight * gameOptions.gameWidth / windowWidth;

    // creation of the game istelf
    game = new Phaser.Game(gameOptions.gameWidth, gameHeight);

    // game states
    game.state.add("PreloadGame", preloadGame);
    game.state.add("PlayGame", playGame);
    game.state.start("PreloadGame");
}
var preloadGame = function(game){}
preloadGame.prototype = {
    preload: function(){

        // making the game cover the biggest window area possible while showing all content
        game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
        game.scale.pageAlignHorizontally = true;
        game.scale.pageAlignVertically = true;
        game.stage.disableVisibilityChange = true;
        game.load.image("tile", 'tile.png');
        game.load.image("hero", 'hero.png');
    },
    create: function(){
        game.state.start("PlayGame");
    }
}
var playGame = function(game){}
playGame.prototype = {
    create: function(){

        // useful to count travelled distance
        this.moves = 0;

        // determining tile size, according to game height and the amount of vertical tiles we want
        this.tileSize = game.height / gameOptions.verticalTiles;

        // amount of placed tiles, useful to tint even/odd tiles with different colors
        var placedTiles = 0;

        // horizontal offset to keep tiles centered in the game
        var offsetX = (game.width - this.tileSize * 3) / 2;

        // array which will contain all tiles
        this.tileArray = [];

        // group which will contain all tiles
        this.tileGroup = game.add.group();

        // placing the group to have tiles centered in the game
        this.tileGroup.x = offsetX;

        // creation of a tween which will scroll the terrain down by one tile
        this.tileTween = game.add.tween(this.tileGroup).to({
            y: this.tileSize
        }, 100, Phaser.Easing.Linear.None);

        // since the endless runner thing is a fake, once we moved the terrain down by a tile
        // we reset its position, then move the lowest tiles to the top, giving the idea of an
        // infinite terrain
        this.tileTween.onComplete.add(function(){
            this.tileGroup.y = 0;
            this.tileGroup.forEach(function(child){
                child.y += this.tileSize;
            }, this);
            for(var i = 0; i < 3; i++){
                this.tileArray[this.moves % this.tileArray.length][i].y -= (gameOptions.verticalTiles + 1) * this.tileSize
            }
            this.moves ++;
        }, this);

        // placing and tinting terrain tiles
        for(var i = 0; i < gameOptions.verticalTiles + 1; i ++){
            this.tileArray[i] = [];
            for(var j = 0; j < 3; j ++){
                var tile = game.add.sprite(j * this.tileSize, game.height - i * this.tileSize, "tile");
                tile.anchor.set(0, 1);
                tile.width = this.tileSize;
                tile.height = this.tileSize;
                tile.tint = gameOptions.tileColors[placedTiles % 2];
                this.tileGroup.add(tile);
                this.tileArray[i][j] = tile;
                placedTiles ++;
            }
        }

        // column numvers ramge from 0 to 2. Hero starts at column 1, the one in the middle
        this.heroColumn = 1;

        // at the moment the hero can move
        this.heroCanMove = true;

        // adding and sizing hero sprite
        this.hero = game.add.sprite(this.tileGroup.x + this.tileSize, game.height - 2 * this.tileSize, "hero");
        this.hero.width = this.tileSize;
        this.hero.height = this.tileSize;
        this.hero.anchor.set(0, 1);

        // tween to move the sprite
        this.heroTween = game.add.tween(this.hero);

        // callback function to be called once the tween is complete
        this.heroTween.onComplete.add(function(){
            this.heroCanMove = true;
            this.hero.x = this.tileGroup.x + this.tileSize * this.heroColumn;
            this.heroWrap.visible = false;
        }, this);

        // and this is the second hero sprite, the one we will use to create the wrap effect
        this.heroWrap = game.add.sprite(this.tileGroup.x + this.tileSize, game.height - 2 * this.tileSize, "hero");
        this.heroWrap.width = this.tileSize;
        this.heroWrap.height = this.tileSize;
        this.heroWrap.anchor.set(0, 1);
        this.heroWrap.visible = false;
        this.heroWrapTween = game.add.tween(this.heroWrap);

        // mask to hide both hero and wrapHero once outside the path of tiles
        var mask = game.add.graphics(this.tileGroup.x, this.tileGroup.y);
        mask.beginFill(0xffffff);
        mask.drawRect(0, 0, this.tileSize * 3, game.height);
        this.hero.mask = mask;
        this.heroWrap.mask = mask;

        // waiting for player input
        game.input.onDown.add(this.moveHero, this);
    },
    moveHero: function(e){

        // can the hero move?
        if(this.heroCanMove){

            // start the tween which moves the terrain
            this.tileTween.start();

            // the hero can't move at the moment
            this.heroCanMove = false;

            // setting hero direction to left if the player clicked/touched the left half of the canvas, or right otherwise
            var direction = e.position.x < game.width / 2 ? -1 : 1;

            // calculating hero next column
            var nextColumn = Phaser.Math.wrap(this.heroColumn + direction, 0, 3);

            // setting hero tween timeline to an empty array to prevent adding waypoints with "to" method
            this.heroTween.timeline = [];

            // new hero destination
            this.heroTween.to({
                x: this.hero.x + this.tileSize * direction
            }, 100, Phaser.Easing.Cubic.InOut, true);

            // this is the case with the wrapping hero coming into play
            if(Math.abs(nextColumn - this.heroColumn) != 1){

                // making it visible
                this.heroWrap.visible = true;

                // placing it outside the final column
                this.heroWrap.x = nextColumn == 0 ? this.tileGroup.x - this.tileSize: this.tileGroup.x + 3 * this.tileSize;

                // resetting tween timeline
                this.heroWrapTween.timeline = [];

                // finally making the wrap hero move
                this.heroWrapTween.to({
                    x: this.heroWrap.x + this.tileSize * direction
                }, 100, Phaser.Easing.Cubic.InOut, true);

            }
            this.heroColumn = nextColumn;
        }
    }
}

Next time I will use the vertical black bars to place the GUI, meanwhile 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

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