Tiled Generated Map with Phaser 3

Junhong Wang
6 min readJun 19, 2020

--

This is a series of posts that attempt to create more beginner friendly tutorials for phaser 3, inspired by Modular Game Worlds in Phaser 3.

Source: Dungeon tileset II by 0x72

By the end of this post, you will have something like this.

Download the Assets

You can download the assets you need from the codepen below. Check the folder named assets in the root directory.

You can press W(up), A(left), S(down), D(right) to move

The Basics

Read Modular Game Worlds in Phaser 3 (Tilemaps #1) — Static Maps to learn the basics of Tilemap API. The article is written by the guy who developed the API.

The TypeScript Approach

In this section, you will learn how to achieve a similar result from the Modular game tutorial using TypeScript. I will assume that you are using the Phaser 3 TypeScript template from the previous post. First, create a TilemapScene class.

You will pack all the tilemap related logic into this class. A little bit of recap on the scene’s life cycle: When the game starts, Phaser calls the constructors of the scenes in the scene list configured in main.ts . init() is called every time the scene starts, which is a good place to initialize the member variables. Then preload() is called to load all the assets needed. Then create() is called to create the game objects to render. Finally, the scene enters the game loop, where update() is called every frame to update the states of the game objects.

First we need to tell TilemapScene where to load the tilemap json file and tileset image file.

tilemapKey is defined such that the tilemap json file is located at <tilemapKey>.json . Similarly, tilesetKey is defined such that the tileset image file is located at <tilesetKey>.png . You will see why you want to do this in a second. In this case, you should have your tilemap json at assets/map.json and tileset image at assets/tiles.png .

Now preload the tilemap and tileset.

The first argument of this.load.image() is the key for the image to load. You will use this key to refer to the image later in the program. The second argument is the file path to the asset. By default, it is <key>.png , which is why we defined the keys the way we did earlier. Throwing away a little bit of flexibility, this makes the code short and clean. Similarly for this.load.tilemapTiledJSON() , the default second argument is <key>.json .

Next, create the layers.

Note the layer names BottomLayer , MiddleLayer , andTopLayer must match how you named them in Tiled. By default, Tiled uses the file name (excluding the extension) as the tileset name when you import a tileset to a tilemap. So if you import a tileset located at path/to/tileset/foo.png , then the default tileset name is foo . getDefaultTilesetName() is just a helper function that extracts the file name from file path. Now if you refresh your browser, you should see the tilemap!

Animated Tile

If you look at the tileset, you see there is a tile animation for the lava.

To learn how to place an animated tile, check this tutorial out. Unfortunately, Phaser 3’s Tilemap API does not support tile animation, so we need to create our own. The good news is it’s not that hard if we assume the duration of the animation is the same across all the frames. The animation data is stored in tileset.tileData like this.

tileset.tileData

tileData is a key-value pairs where the key is the tileid and the value is the animation data. The screenshot above says the tile with id 9 has animation, where the first frame is a tile with id 9, the second frame is tile with id 10, the third frame is a tile with id 11, and each frame should last 100ms.

To change the tile to render, it’s as simple as setting the id property of the tile object. The question is when to change it. Recall a scene’s update() method takes two arguments — time and delta . time is current time since the game starts and delta is the elapsed time since previous frame. Here’s the idea: We can make use of delta and create a variable that loops from 0 ms to 300 ms. If the value is between 0 ms and 100 ms, we render the first frame; If the value is between 100 ms and 200 ms, we render the second frame; and so on. We create a AnimatedTile class that implements this logic.

tileset.firstgid is the first tile id that belongs the tileset. firstgid of the first tileset is always 1 while the first tile id in Tiled is always 0. That’s why the tile id is offset by firstgid in the code above.

In TilemapScene , we are going to check every tile and see if the tile has animation data associated with it.

Notice we have changed the layers from static layer to dynamic layer because we are changing the tile id. Finally, update the animatable tiles every frame.

Now you should see the lava is animating!

Adding a Player (Optional)

Let’s create a player that explores the fantastic world we just created. First, we need to preload the asset.

Just like all other loading methods, the first argument of this.load.atlas is the key of the asset. The second argument is the texture whose default value is <key>.png . The third argument is the atlas json file of the texture whose default value is <key>.json . So make sure you have assets/elf_f.png and assets/elf_f.json .

Here’s the player class.

You are probably already familiar with what’s going on in these code if you have followed the Phaser 3 Official Tutorial.

Insert the player right after you created the middle layer, but before the top layer. This makes sure top layer always appears on top of the player.

this.physics.add.collider() adds the collision detection between the first argument and the second argument, making sure they are separated when they collide.

Finally add these configuration code at the end of the create() function.

Set the bounds of the world and the camera so that the player can never go off the screen. If your game becomes blurry, you need to configure Phaser and disable antialiasing.

Add this code snippet to the game config in main.ts .

And that is it! Now you can start adding more features to your game.

Next Up

In the next part, you will learn how to do scene transition. Cheers!

--

--

Junhong Wang

I'm Junhong. I'm a Software Engineer based in LA. I specialize in full stack web development and writing readable code. junhong.wang