Modular Game Worlds in Phaser 3 (Tilemaps #1) — Static Maps

This is a series of blog posts about creating modular worlds with tilemaps in the Phaser 3 game engine. In this first post, we’ll go from zero to creating a Pokemon-style top down game world that a player can explore:

Final example we’ll create — graphics from Tuxemon.

The next post covers how to create a dynamic platformer and the posts after that will cover procedurally generated dungeons and wall-jumping maps with Matter.js.

Before we dive in, all the code that goes along with this post is in this repository. These tutorials use the latest version of Phaser (v3.16.2) and Tiled (v1.2.2) as of 02/26/19.


Intended Audience

Alright, Let’s get into it!

What is a Tilemap

Imagine trying to recreate Mario from scratch. Let’s say we decide to try loading each level as a giant image file. World 1–1 would be over 3500px wide:

We’d need a lot of pixels to store that first level and the other 31 levels in the NES game as images. Additionally, it would be hard to sync up the image with logic with the game. Which pixels can Mario stand on? Which pixels correspond to pipes he can enter?

The tilemap approach defines a set of modular, regularly-sized tiles that we can use to build our levels. That way, we only need one image, a tileset:

Source: Tileset by Nintendo, remixed by Arrow

So that 304px x 192px image holds the possibility of recreating all the levels of the original mario game, plus any new levels you can imagine. (*Of course, you’d still be missing a mustached man and a bipedal turtle, among other things.) Each tile is just 16 x 16 pixels. An arrangement of those tiles into a level is called a tilemap. With tilemap editing software, we can easily configure properties of the tiles too. For example, we can mark some tiles — like the ground tiles — as solid tiles that Mario can stand on.

So with tilemaps, we’ve got a smaller image (performance & memory win) that we can use to easily create and iterate on level designs (creative win).

Phaser 3 Basic Template

This is a template that you’ll see throughout the Phaser examples repository. It’s an easy way to get started. It creates a game and defines a scene as a collection of functions — preload, create and update.

Here’s a slightly more complicated example that shows how to load and create a background and some text:

Check out the codepen, live example or the source code here.

There might be a lot of new concepts in this series if you haven’t used Phaser, so check out the extensive examples and documentation if you get lost.

First Step

We’ll start with the boilerplate from the last section. Inside of preload, we can load up the tileset image:

this refers to our current scene and this.load is the scene's loader which handles, well, the loading of assets. The create function won't get run until after all the assets in preload are done loading.

level is just a 2D array of numbers, or indices, that point to a specific tile from our tileset. 0 is the top left tile, 1 is the one next to it, etc.

Mapping from tileset index to tile

Note: an index that is less than zero is considered an empty tile.

Check out the codepen, live example or the source code here.

Breaking down that code, we’ve got three main parts: a Tilemap, a Tileset and a StaticTilemapLayer. You create a Tilemap through this.make.tilemap (or this.add.tilemap). This isn't a display object, rather, it holds data about the map and allows you to add tilesets & tilemap layers.

A map can have one or more layers, which are the display objects that actually render tiles from a Tileset. They come in two flavors: StaticTilemapLayer & DynamicTilemapLayer. A StaticTilemapLayer is super fast, but the tiles in that layer can’t be modified and can’t render per-tile effects like flipping or tint. A DynamicTilemapLayer trades some speed for the flexibility and power of manipulating individual tiles. For this post, we'll stick to static layers, but next time, we'll dive into dynamic layers.

Loading from a File: CSV

Note: this example is basically a copy of a Phaser example which features Rich Davey & Ilija Melentijević’s Cat Astro Phi assets.

Check out the codepen, live example or the source code here.

We can easily add some interactivity here by letting the player pan around the world using Phaser’s camera system. The code is commented to explain the new pieces of Phaser used, but check out the Phaser camera examples for more on cameras.

Check out the codepen, live example or the source code here.

Building a Map in Tiled

We won’t dive into how to use Tiled — that’s an expansive topic by itself — so check out Tiled’s documentation and the Game from Scratch tutorial series for a crash course. You can also download the tilemaps (.tmx files) and tilesets from the demos here. Open them up, play around and you should get the hang of it.

When working with Tiled to generate maps for Phaser, there are a few things you’ll want to make sure to do:

  1. When you load a tileset into your map, make sure to check the “Embed in map” option. (If you forget to do this, then you can click the embed tileset button the bottom of the screen.) See first two images below.
  2. Make sure you aren’t using a compressed “Tile Layer Format.” You can adjust that in map properties sidebar… which you can open by hitting “Map → Map Properties” in the top toolbar. See third image below.
  3. When you export your map, save it as a JSON file.
Embedding a tileset when creating it
Embedding a tileset AFTER creating it
Changing the tilemap format

Important note! A recent release of Tiled (version 1.2) changed the map export format in a way that breaks Phaser’s map importing. If you are running into problems loading your maps (especially if collision info isn’t loading), make sure you are using at least Phaser 3.14.0.

Loading a Tiled Map

This step is mainly about connecting up data. To help making the naming slightly more clear, here’s where the names come from:

You’ll notice that the map is composed of multiple layers placed on top of one another:

This is a common design pattern when working with Tiled. It allows us to separate out elements to be placed at different “depths” in the game. With these layers, we can ensure the “Below Player” layer (the ground & path) are displayed under the player sprite and the “Above Player” layer (roof/statue/sign tops) are displayed on top of the player sprite. The “World” layer has all the rest of the stuff, including the colliding/solid stuff in the world.

If we add in our camera code, we end up with:

Check out the codepen, live example or the source code here.

Moving with Physics

In AP, you can create physics bodies that are either rectangles or circles. Rectangle bodies are axis-aligned bounding boxes, which roughly means they can’t be rotated. Colliding tiles in our map loaded up with AP will be given a rectangular body that matches the size of the tile.

There are four things we’ll need to do:

  1. Mark certain tiles in the worldLayer as colliding so that AP knows to use them for collisions.
  2. Enable the AP physics engine.
  3. Create a physics-based sprite for the player.
  4. Set the player to collide with the worldLayer.

The first step to use a tilemap with physics is that you need to mark which tiles should be solid (“colliding”). One way to do that would be to mark certain tile indices as colliding within a layer:

If you are working with tile indices, then there’s setCollision, setCollisionBetween and setCollisionByExclusion. But thinking in terms of indices is hard, so there's a better way: setCollisionByProperty. Tiled allows you to add properties to a tileset via the Tileset Editor, so we can just mark which tiles collide directly in Tiled.

Steps (or see GIF below):

  1. Open up the Tileset Editor by clicking on the “Edit Tileset” button (at the bottom right of the screen).
  2. Click and drag (or CTRL + A) to select all the tiles.
  3. Under the properties window (left side of the screen), click the plus icon and add a boolean property named “collides.”
  4. Select only the tiles that you want to collide and set “collides” to true by checking the box
  5. Re-export your map.

Back inside of Phaser, we can simply do the following to mark our colliding tiles within worldLayer:

If you want to verify that you’ve got the right tiles marked as colliding, use the layer’s debug rendering:

Which will look like this:

Once we’ve got tiles marked as colliding, we can add physics. In our game’s config, we can turn on the arcade physics engine by doing the following:

We can create a simple player sprite that moves around using physics:

Note: I’m using a texture atlas here. See this tutorial for more info.

The last step is to collide the player and the tilemap layer against one another. We could use collide or addCollider. We'll go with the latter:

And putting it all together, with a few extras like adding in player animations:

Check out the codepen, live example or the source code here.

There’s a whole lot of powerful stuff you can do with Tiled and Phaser to make the creative process of developing a game world easier. For example, the code for this section uses an object layer to embed the player’s spawn point directly in the map.

But that’s just scratching the surface! Keep an eye out for the next post, where we’ll dive into dynamic tilemap layers and creating a procedural dungeon.

Next Up

Thanks for reading, and if there’s something you’d like to see in future posts, let me know!

Addendum on Tile Bleeding

About Me

Developer, Artist & Educator - Learning Product Developer at Convergence Design Lab

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store