HTML5 Phaser Tutorial – Top-Down Games with Tiled

I’m a huge fan of top-down 2D games, mainly RPG’s, adventure games and dungeon crawlers.

In this tutorial we’ll create a basic template you can use to make your own top-down games using Phaser, an awesome HTML5 game framework.

This template will be expanded in further tutorials to add the ability to move between levels, work on mobile and more cool stuff. If there are aspects you’d like me to cover in future tutorials feel free to post that on the comments!

We’ll be using a free map editor called Tiled to create our level. One nice thing about Phaser is that you can easily load map files exported from Tiled (JSON format).

This tutorial builds on top of the concepts covered in our previous Phaser tutorial, so if you are not familiar with States, preloading assets and rendering sprites you might want to check that tut first.

Learn Phaser by building 15 games

If you want to master Phaser and learn how to publish Phaser games as native games for iOS and Android feel free to check Zenva‘s online course The Complete Mobile Game Development Course – Build 15 Games.

Source code files

You can grab them from here (updated to version 2.4.2). If you want us to notify you when we release new tutorials on Phaser or HTML5 game development don’t forget to click “subscribe to our list” when downloading the files (if you choose not to you won’t hear from us!).

Tutorial goals

1-Learn to create levels for your game using Tiled and load them using Phaser.

2-Learn how to create a basic top-down game starter.

Concepts covered

-Setting the game size in pixels
-Tiled basic usage
-Tile layers and object layers
-Tilemaps
-Creating sprites from object layers
-Moving a character in a top-down level

Tutorial requirements

-This tutorial builds on the concepts covered in our previous Phaser tutorial HTML5 Phaser Tutorial – SpaceHipster, a Space Exploration Game, so it assumes you know how to setup a game, work with States, preload game assets and create sprites.

-Basic to intermediate knowledge of JavaScript. If you need a refreshment feel free to check our JavaScript beginners video course at Zenva Academy.

-A code editor or IDE. My personal preference at the moment is Sublime Text.

-Get Tiled, an Open Source and free level editor.

-Grab Phaser from it’s Github repo. You can either clone the repo or download the ZIP file.

-You need to run the code and Phaser examples using a web server. Some popular options are Apache (WAMP if in Windows, MAMP if in Mac). Some lightweight alternatives are Mongoose web server and Python’s HTTP server. Take a look at this guide for more details.

-Download the full tutorial source code and game assets here. If you want us to let you know when we release new tutorials on Phaser or HTML5 game development don’t forget to subscribe to our list!

-Have the documentation and the examples page at hand. Also don’t forget that you can always find answers by looking at the source code of Phaser itself!

Get Tiled-Face

Tiled is a really awesome level editor created by Thorbjørn Lindeijer which allows you to visually create tile-based levels for your games. The main file format these levels use is TMX. In order to use these levels in Phaser we need to export them as JSON.

When I talk about “tiles” and “tiled-based games” what I mean are games where the levels are composed of individual small blocks or pieces. Check out the Wikipedia definition if you are not 100% familiar with this concept.

To create a new map in Tiled go to File -> New:

tiled tutorial create new map

On width and height specify the number of tiles your level will have on each dimension. Tile size refers to the size of the individual tiles. Make sure Orientation is set to “Orthogonal” and Layer format to “CSV”.

The next step is to create the Layers. A level will have different layers that sit on top of each other. The naming of the layers is important here as we need to refer to that in the code later on.

There are two types of layers:

-Tile layer: layer made of tiles/blocks.
-Objects layer: layer where you create vector objects that can contain metadata.

You can create layers using the “+” button under layers, to rename them just click on the name of the newly created layers. The order matters, you can rearrange them using the arrows.

tiled create layers

In our example we’ll have two tile layers: one for the background called backgroundLayer (doesn’t collide with the player) and one for the blocking elements called blockedLayer (walls, trees, etc). We’ll also have one objects layer (objectsLayer) that will represent game elements, in this case the player starting location in the level, the collectables/items, a door (which we’ll extend in a future tutorial). In this layer you’d want to add enemies and more stuff. This is all just a suggestion and how I normally do it, it is by no means a convention or a rule of the framework.

Loading the tileset

A tileset is a set of individual tiles, which come in an image. You can have many tilesets on your tilemaps. We’ll use just one: assets/images/tiles.png.

To load a tileset: Map -> New Tileset

tiled add tileset

The nice retro tileset we’ll use is from Open Game Art and it’s a public domain image (CC0), so you can use it for both non-commercial and commercial projects.

The tiles are 16×16 and there is no separation between them or margin. The “name” in the text field, keep that name in your head as we need to refere to it when joining the tileset with the map in Phaser.

Create the level

You can now create your level. Now it’s really up to you. What I did was fill the backgroundLayer with a black tile (using the bucket tool), then changed to the blockedTile and painted some walls and trees:

tiled simple level

The object layer

We’ll add 3 different types of objects or game elements: items that will be collected by the player, the player starting position, and a door that will take you to a new level (to be implemented in a follow-up tutorial).

These objects will be represented by sprites from the tileset, but that is just for OUR simplicity. Phaser will show the sprite you tell it to when creating a Sprite object.

To insert new objects: select the objectsLayer layer, click on the Insert Tile button (the one that shows a picture of the sunset) then click on the tile you want to show, then click on the location of the object within the map.

tiled create object

We’ll add some blue and green teacups, a door and a “player”:

tiled map level

Adding properties to the objects

How is Phaser going to know what these elements represent and what sprite should be used to display them? Lets add that as object properties.

Click on the “Select object” button from the menu, then double click on any object and you’ll see a dialog where you can enter properties.

Enter a property called “type” with value “item” to the tea cups (this is not the “type” select dropdown!, this is within the Properties area), “playerStart” for the player location and “door” to the door.

For the blue cups enter a property “sprite” with value “bluecup”, “greencup” for the green cup, “browndoor” to the door. None to the player start as it won’t represent a sprite, but a location for us to use. These values are the sprite keys we’ll implement in Phaser for the corresponding image assets (peak at /assets/images).

**You can also just create one of each element, then select the element with the right click and click “duplicate”, this will allow you to quickly replicate an object and then drag it to it’s final position.

We won’t implement the door behavior in this tutorial, but enter these properties so you can guess where I’m going with this:

door properties

Before we move any further, I’d like to make it clear that this will work with the game template we are building in this tutorial, it is not a default Phaser functionality. Phaser only provides the ability to create several sprites from objects using the same sprite key. Take a look at the Tilemap documentation to learn what Phaser provides in this aspect.

Once you are doing export the level as a JSON file. File -> Export As -> Json Files, save into /assets/tilemaps.

Hola Mundo Phaser

I’ll go through this section without stepping into much details as these concepts were covered in the previous Phaser tutorial.

In this game we’ll have three states: Boot, Preload and Game. You can always add a main menu screen as we did in the previous tutorial.

Let’s start by creating index.html, where we’ll include the game files:

	
		
		

		



		  body {
		    padding: 0px;
		    margin: 0px;
		  }
		  

 

 

	  
		
		
	

I prefer using the non-minified version of Phaser when doing development (phaser.js) as it makes it easier to debug errors. When working with HTML5 game development libraries it’s good practice to constantly refer to the source code itself to find answers. Not everything can be found on StackOverflow and getting familiar with the framework’s code itself can save you hours.

main.js creates the game itself and defines the game size in pixels, which I’ve set to a very small size for this example. The reason being, the tileset I’ll be using is also small and I wanted to have the game not showing the entire level (which is not that big) in the browser space.

By creating all the game objects inside a parent object called TopDownGame (this is what’s called the namespace pattern) we don’t pollute the global scope with our game elements. This is good practice as it could happen that you are including some other libraries in your project and their names match those of the objects you are creating so then you have a conflict!. Whereas if you put everything inside TopDownGame there won’t be any conflicts, unless another library created an object called TopDownGame in the global scope which is unlikely.

(Btw this:

var TopDownGame = TopDownGame || {};

basically means that if TopDownGame exists, then use it, if it doesn’t exist then initiate it as an empty object)

var TopDownGame = TopDownGame || {};

TopDownGame.game = new Phaser.Game(160, 160, Phaser.AUTO, '');

TopDownGame.game.state.add('Boot', TopDownGame.Boot);
TopDownGame.game.state.add('Preload', TopDownGame.Preload);
TopDownGame.game.state.add('Game', TopDownGame.Game);

TopDownGame.game.state.start('Boot');

Boot.js

In Boot.js we’ll create the Boot state, where we load the images for the loading screen (just a loading bar in this example) and define other game-level configurations.

Since we are using Phaser.ScaleManager.SHOW_ALL the game will show as large as it can to fit the browser space, but it wont’ show more than 160×160 pixels of the game world, as that’s the limit we defined in main.js.

var TopDownGame = TopDownGame || {};

TopDownGame.Boot = function(){};

//setting game configuration and loading the assets for the loading screen
TopDownGame.Boot.prototype = {
  preload: function() {
    //assets we'll use in the loading screen
    this.load.image('preloadbar', 'assets/images/preloader-bar.png');
  },
  create: function() {
    //loading screen will have a white background
    this.game.stage.backgroundColor = '#fff';

    //scaling options
    this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    
    //have the game centered horizontally
    this.scale.pageAlignHorizontally = true;
    this.scale.pageAlignVertically = true;

    //physics system
    this.game.physics.startSystem(Phaser.Physics.ARCADE);
    
    this.state.start('Preload');
  }
};

Preload.js

Preload.js will contain the loading of the assets, including the loading of the tilemap. See how the tileset iteself is simply loaded as another sprite. It will be later on when we relate the tilemap level with the tileset:

var TopDownGame = TopDownGame || {};

//loading the game assets
TopDownGame.Preload = function(){};

TopDownGame.Preload.prototype = {
  preload: function() {
    //show loading screen
    this.preloadBar = this.add.sprite(this.game.world.centerX, this.game.world.centerY + 128, 'preloadbar');
    this.preloadBar.anchor.setTo(0.5);

    this.load.setPreloadSprite(this.preloadBar);

    //load game assets
    this.load.tilemap('level1', 'assets/tilemaps/level1.json', null, Phaser.Tilemap.TILED_JSON);
    this.load.image('gameTiles', 'assets/images/tiles.png');
    this.load.image('greencup', 'assets/images/greencup.png');
    this.load.image('bluecup', 'assets/images/bluecup.png');
    this.load.image('player', 'assets/images/player.png');
    this.load.image('browndoor', 'assets/images/browndoor.png');
    
  },
  create: function() {
    this.state.start('Game');
  }
};

Game.js

The following code in the create() method will show the map (without the objects) on your browser:

var TopDownGame = TopDownGame || {};

//title screen
TopDownGame.Game = function(){};

TopDownGame.Game.prototype = {
  create: function() {
    this.map = this.game.add.tilemap('level1');

    //the first parameter is the tileset name as specified in Tiled, the second is the key to the asset
    this.map.addTilesetImage('tiles', 'gameTiles');

    //create layer
    this.backgroundlayer = this.map.createLayer('backgroundLayer');
    this.blockedLayer = this.map.createLayer('blockedLayer');

    //collision on blockedLayer
    this.map.setCollisionBetween(1, 100000, true, 'blockedLayer');

    //resizes the game world to match the layer dimensions
    this.backgroundlayer.resizeWorld();
  }
}

The first thing we did was create a Tilemap object from the loaded JSON file. What’s a map without the images? we need to add the tileset image to this map, which we do with this.map.addTilesetImage('tiles', 'gameTiles');, where “tiles” is the name we gave the tileset in Tiled, and “gameTiles” is the image key as defined in Preload.js (I made those two different in purpose to make it more clear what they are referring to).

Then we load the layers, and make the blockedLayer “collision enabled”. The number between 1 and 2000 is an index range for the tiles for which we want to enable collision (in this case such a big number should include them all which is what I intended). In order to obtain this number open level1.json and in “layers” – “data” of the two layers find the largest number you can find, in this case 1896 so I put 2000 just to be sure I didn’t miss a slightly larger number. So in theory, you could just use one single layer, and define certain tiles as collision-enabled by specifying their index range.

WARNING: A previous version of this tutorial said put 100000 instead of 2000. The problem with putting unnecessary large numbers is that you are running more loops affecting the performance of your game

I find it easier to work with one layer for the background and one for the collision elements as I don’t want to count tiles one by one, that is just my preference here.

Lastly, we make the world the same size as the map.

Finding objects by type

Remember that “type” properties we gave to the objects in the level? Well I’d like to be able to find the objects of a certain type, so we’ll build a utility method in Game.js for that.

Tilemap objects have a “objects” property which is an array with the objects of each layer (you could have many object layers!). We’ll use this method to find the objects that have the property “type” of the value we want. I recommend you take a look at the file /src/Tilemap.js from the downloaded Phaser library and to the Tilemap documentation. You can also just use this method but it’s always better to understand where it came from.

  //find objects in a Tiled layer that containt a property called "type" equal to a certain value
  findObjectsByType: function(type, map, layer) {
    var result = new Array();
    map.objects[layer].forEach(function(element){
      if(element.properties.type === type) {
        //Phaser uses top left, Tiled bottom left so we have to adjust the y position
        //also keep in mind that the cup images are a bit smaller than the tile which is 16x16
        //so they might not be placed in the exact pixel position as in Tiled
        element.y -= map.tileHeight;
        result.push(element);
      }      
    });
    return result;
  },

When calling this method, it will give us an array of objects as they are represented in the assets/tilemaps/level1.json file.

Create sprites from Tiled objects

We are able to find tiled objects by type, and now we’d like to create sprites for them. This method in Game.js will help us out. This will become more clear over the next section where we create the tea cups.

//create a sprite from an object
  createFromTiledObject: function(element, group) {
    var sprite = group.create(element.x, element.y, element.properties.sprite);

      //copy all properties to the sprite
      Object.keys(element.properties).forEach(function(key){
        sprite[key] = element.properties[key];
      });
  },

We are copying all the properties you might have entered in Tiled. For example for the door we entered a bunch of properties. Well, this will all be copied to the sprite. Even if you entered sprite properties such as “alpha” (for transparency), this will be moved along to the sprite you are creating here.

** We are not using Tilemap.createFromObjects here because I wanted to be able to specify the type and the sprite inside of Tiled. If createFromObjects is what you need for your game I recommend you use that!

I love tea

Let’s create the tea cups! they’ll be rendered after this:

var TopDownGame = TopDownGame || {};

//title screen
TopDownGame.Game = function(){};

TopDownGame.Game.prototype = {
  create: function() {
    this.map = this.game.add.tilemap('level1');

    //the first parameter is the tileset name as specified in Tiled, the second is the key to the asset
    this.map.addTilesetImage('tiles', 'gameTiles');

    //create layer
    this.backgroundlayer = this.map.createLayer('backgroundLayer');
    this.blockedLayer = this.map.createLayer('blockedLayer');

    //collision on blockedLayer
    this.map.setCollisionBetween(1, 100000, true, 'blockedLayer');

    //resizes the game world to match the layer dimensions
    this.backgroundlayer.resizeWorld();

    this.createItems();

  },
  createItems: function() {
    //create items
    this.items = this.game.add.group();
    this.items.enableBody = true;
    var item;    
    result = this.findObjectsByType('item', this.map, 'objectsLayer');
    result.forEach(function(element){
      this.createFromTiledObject(element, this.items);
    }, this);
  },
  //find objects in a Tiled layer that containt a property called "type" equal to a certain value
  findObjectsByType: function(type, map, layer) {
    var result = new Array();
    map.objects[layer].forEach(function(element){
      if(element.properties.type === type) {
        //Phaser uses top left, Tiled bottom left so we have to adjust
        //also keep in mind that the cup images are a bit smaller than the tile which is 16x16
        //so they might not be placed in the exact position as in Tiled
        element.y -= map.tileHeight;
        result.push(element);
      }      
    });
    return result;
  },
  //create a sprite from an object
  createFromTiledObject: function(element, group) {
    var sprite = group.create(element.x, element.y, element.properties.sprite);

      //copy all properties to the sprite
      Object.keys(element.properties).forEach(function(key){
        sprite[key] = element.properties[key];
      });
  }
};

Doors

Adding the door will follow the same approach:

this.createItems();
this.createDoors();   

and:

createDoors: function() {
    //create doors
    this.doors = this.game.add.group();
    this.doors.enableBody = true;
    result = this.findObjectsByType('door', this.map, 'objectsLayer');

    result.forEach(function(element){
      this.createFromTiledObject(element, this.doors);
    }, this);
  },

But we can’t move around yet and find it.

The player

We are representing the player start position with an object of type “playerStart”. What we’ll do is find that object, then create the player sprite on that location and set the camera to focus on the player, and activate cursor keys to move the player with the arrows (to be implemented shortly):

 //create player
var result = this.findObjectsByType('playerStart', this.map, 'objectsLayer')

//we know there is just one result
this.player = this.game.add.sprite(result[0].x, result[0].y, 'player');
this.game.physics.arcade.enable(this.player);

//the camera will follow the player in the world
this.game.camera.follow(this.player);

//move player with cursor keys
this.cursors = this.game.input.keyboard.createCursorKeys();

Player movement

Listen to the cursor keys and adjust the player velocity accordingly. We’ll do this in the update() method:

update: function() {
    //player movement
    this.player.body.velocity.y = 0;
    this.player.body.velocity.x = 0;

    if(this.cursors.up.isDown) {
      this.player.body.velocity.y -= 50;
    }
    else if(this.cursors.down.isDown) {
      this.player.body.velocity.y += 50;
    }
    if(this.cursors.left.isDown) {
      this.player.body.velocity.x -= 50;
    }
    else if(this.cursors.right.isDown) {
      this.player.body.velocity.x += 50;
    }
  },

Now you can move your player around! but there is no collision yet.

Collision

Add the following to update(). This works in the same way as we covered in the previous tutorial, but see how you can also just put in a layer, as we are doing with this.blockedLayer:

//collision
this.game.physics.arcade.collide(this.player, this.blockedLayer);
this.game.physics.arcade.overlap(this.player, this.items, this.collect, null, this);
this.game.physics.arcade.overlap(this.player, this.doors, this.enterDoor, null, this);

Implementing collect():

collect: function(player, collectable) {
    console.log('yummy!');

    //remove sprite
    collectable.destroy();
  },

And enterDoor(), which is something left for a follow-up tutorial:

enterDoor: function(player, door) {
    console.log('entering door that will take you to '+door.targetTilemap+' on x:'+door.targetX+' and y:'+door.targetY);
  },

And that’s it!

We can now create a level in Tiled, load it in Phaser and move around a character around with the cursor keys!

phaser tutorial top-down games with Tiled

Feel free to use this code for your own games, and it’s always nice if you link back to us :)

If you haven’t already, you can download this tutorial game files and assets here.

Where to go next?

You can check our online course HTML5 Mobile Game Development with Phaser for a comprehensive training on this awesome framework, created by professional game developer Codevinsky.

HTML5 Game Development with Phaser

These other courses on HTML5 game development and JavaScript that might be of your interest as well!

Also, feel free to share your thoughts in the comments and let us know what else you’d like us to write about!