Phaser tutorial: HTML5 player movement as seen in iPad Magick game, using mostly tile maps

Read all posts about "" game

Some time ago I downloaded an iPad game called Magick.

It’s a retro platformer where a mage called Oz walks and jumps by himself, and you can summon a crate, but only one, to modify the terrain.

The way you control the player, which actually you don’t control, you can just make some changes to the level, is really interesting to build with Phaser.

Since the game is easier to play than to describe, have a look at what you are going to build:

The player walks and climbs on his own, you can only click anywhere on an empty spot to summon a crate, but you can submit only one crate at once. Will you be able to reach the upper platform on the left?

Before I show you the fully commented source code, please note the level has been built with Tiled and exported as JSON. I am telling you this because some of the names used in the game itself are taken from the JSON data in level map.

Here is the Tiled output:

{ "height":10,
 "layers":[
        {
         "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
         "height":10,
         "name":"myLevel",
         "opacity":1,
         "type":"tilelayer",
         "visible":true,
         "width":25,
         "x":0,
         "y":0
        }],
 "orientation":"orthogonal",
 "properties":
    {

    },
 "tileheight":32,
 "tilesets":[
        {
         "firstgid":1,
         "image":"rock.png",
         "imageheight":32,
         "imagewidth":32,
         "margin":0,
         "name":"rock",
         "properties":
            {

            },
         "spacing":0,
         "tileheight":32,
         "tilewidth":32
        }, 
        {
         "firstgid":2,
         "image":"block.png",
         "imageheight":32,
         "imagewidth":32,
         "margin":0,
         "name":"block",
         "properties":
            {

            },
         "spacing":0,
         "tileheight":32,
         "tilewidth":32
        }],
 "tilewidth":32,
 "version":1,
 "width":25
}

And here is the fully commented source code, just a bunch of lines:

window.onload = function() {	
     
     // the game itself
	var game = new Phaser.Game(800, 320);
	// the player! The hero of the game!!
     var player;
     // level map, created with Tiled
	var map;
     // map layer with level data
	var levelLayer;
     // player horizontal speed
	var playerSpeed = 120;
     // map tile size, in pixels
	var tileSize = 32;
     // tilePoint will be used to save the coordinates of the tile placed by the player
	var tilePoint = null;
     // is the player jumping?
	var playerJumping = false;
     
     // playGame function, to be bound to "PlayGame" state
	var playGame = function(game){}
	
     playGame.prototype = {
          // preloading assets
		preload: function(){
               // map data
			game.load.tilemap("map", "map.json", null, Phaser.Tilemap.TILED_JSON);
			// rock image, used to draw walls
               game.load.image("rock", "rock.png");
               // block image, used to let the player add tiles to the map
			game.load.image("block", "block.png");
               // the player
			game.load.image("player", "player.png");
		},
          // once the game has been created
		create: function(){
               // starting arcade physics
			game.physics.startSystem(Phaser.Physics.ARCADE);
               // adding the map
			map = game.add.tilemap("map");
               // adding "rock" and "block" graphic assets to the map
			map.addTilesetImage("rock");
			map.addTilesetImage("block");
               // both "rock" and "block" are solid
			map.setCollisionBetween(1,2);
               // we are going to use "myLevel" layer, as created with Tiled
			levelLayer = map.createLayer("myLevel");
               // adding the player
			player = game.add.sprite(48,226,"player");
			// setting player registration point in the center
               player.anchor.setTo(0.5);
               // enabling arcade pysics to the player
			game.physics.enable(player, Phaser.Physics.ARCADE);
               // setting player gravity
			player.body.gravity.y = 400;
               // waiting for input, both touch or mouse click, to call addBlock function
			game.input.onDown.add(addBlock, this);		
		},
          // function to be executed at each frame
		update:function(){
               // setting player x speed to zero
			player.body.velocity.x = 0;
               // check for collision between the player and the level, and call "movePlayer" if there's a collision
			game.physics.arcade.collide(player, levelLayer, movePlayer);
		}
	}
	
	function movePlayer(){
          // is the player blocked down, that is: is the player on the floor?
		if(player.body.blocked.down){
               // set player horizontal velocity
			player.body.velocity.x = playerSpeed;
               // the player is definitively not jumping
			playerJumping = false;
		}
          // is player speed greater than zero and the player is blocked right, that is the player is against a wall on the right?
		if(player.body.blocked.right && playerSpeed>0){
               // is the tile on player upper right diagonal empty, as well as the tile immediately above the player, or is the player already jumping?
			if((!map.getTileWorldXY(player.x+tileSize,player.y-tileSize,tileSize,tileSize,levelLayer)&&!map.getTileWorldXY(player.x,player.y-tileSize,tileSize,tileSize,levelLayer)) || playerJumping){
                    // jump
				jump();
			}
			else{
                    // invert player speed
				playerSpeed*=-1;
			}
		}
          // the same concept is applied to collisions on the left side of the player
		if(player.body.blocked.left && playerSpeed<0){
			if((!map.getTileWorldXY(player.x-tileSize,player.y-tileSize,tileSize,tileSize,levelLayer)&&!map.getTileWorldXY(player.x,player.y-tileSize,tileSize,tileSize,levelLayer)) || playerJumping){
				jump();
			}
			else{
				playerSpeed*=-1;
			}
		}	
	}
	
	function addBlock(e){
          // is the tile where we clicked/touched a null tile?
		if(!map.getTileWorldXY(e.x,e.y,tileSize,tileSize,levelLayer)){
               // is there already a tile placed by the player?
			if(tilePoint){
                    // remove the tile placed by the player
				map.removeTileWorldXY(tilePoint.x, tilePoint.y, tileSize, tileSize, levelLayer);	
			}
               // place the tile on mouse/touch position
			map.putTileWorldXY(2, e.x, e.y, tileSize, tileSize, levelLayer);
               // save placed tile position
			tilePoint = new Phaser.Point(e.x,e.y); 		
		}
	}
	
	function jump(){
          // setting player vertical velocity
		player.body.velocity.y = -100;
          // setting player horizontal velocity
		player.body.velocity.x = playerSpeed/4;
          // now the player is jumping	
		playerJumping = true;
	}
	
     // defining "PlayGame" state
	game.state.add("PlayGame",playGame);
     // run "PlayGame" state
	game.state.start("PlayGame")
}

I always say the funniest game are built on the simplest concepts, and here’s another one.

Download the source code and suggest some features to add to the 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