Phaser tutorial: HTML5 player movement as seen in iPad Magick game, using mostly tile maps
Talking about Magick game, Game development, HTML5, Javascript and Phaser.
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.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.
