Creating a DRY Design with Phaser 3

Katelynn Burns
5 min readDec 16, 2020

--

When creating a game with multiple levels, it can feel like you are rewriting the same code over and over. Wouldn’t it be easier to write one template for your level and have the changing data be inserted into this template? This guide will help you to create a DRY design have cleaner looking and easier to write code that avoids this repetition.

The Different Phases of Phaser 3

When a game is created using Phaser 3 it goes through three major steps. The first step is the preload. This is the section where any assets are loaded into the game’s world so that we can use them. Nothing that is in the preload can be seen in the game yet. They are just now available for our game environment to use. The next step is the create. This is where we take any of the assets we added in the preload phase and put them in our game’s physical environment for users to interact with. Sprites now appear on screen, sounds are now played, and most functions that we create will go in this section. The final step is the update. The update is interesting because it repeats and repeats often. If you are curious how often the update runs, throw a console log in this section and watch the number of console logs increase rapidly. (Don’t do this for too long though unless you want to seriously slow down your system.) This is important to think about because we want to be careful when using this section. If it is repeating that rapidly and even a simple console log slows things down so much, it’s important to make sure we’re only putting things in the update that are necessary. Generally the only things to go into this section are updates to player movement.

When in Doubt, Array it Out

Now that we know the different sections of our scene, we have some area to play with. The first question we need to ask ourselves is ‘what changes in each level and what stays the same?’ Chances are, there are going to be parts of our game that stay the same. It might be a bit disconcerting if every time a player starts a new level, they find themselves controlling a new character (if disconcerting is what you are going for then feel free to do this though). The functions we will use for each level, the movements, and the logic of how our game works. These are all things that tend to be consistent for each level. Things that could be different from level to level are map designs, locations of assets, the looks of enemies. The logic behind how each item interacts with the scene and how the player interacts with the item remains the same, but small aspects change between levels to make them each unique. So how do we do this? For maps and sprites, all we have to do is pass a different source for each image in the preload. For instance, when loading a tilemap, we would normally write code that looks something like this:

this.load.tilemapTiledJSON(“map”,‘assets/backgrounds/jsonFiles/levelOne.json’)

Instead what if we created an array containing the source for all of our maps, and passed the load a specific index of this array?

const tileMaps = [‘assets/backgrounds/jsonFiles/levelOne.json’,‘assets/backgrounds/jsonFiles/levelTwo.json’,‘assets/backgrounds/jsonFiles/levelThree.json’];this.load.tilemapTiledJSON(“map”, tileMaps[this.game.level])

This can be done for other types of assets as well. For instance, when we create our player entity we pass it four parameters: this, x position, y position, and the sprite name we created in the preload.

this.player = new Player(this, 160, 190, “player”)

We could pass it a static x and y position and have our player start in the same place on every level. Or we could create an array of objects for each level consisting of x and y keys. And pass that is instead.

const playerStartPosition = [{ x: 11, y: 6 },{ x: 15, y: 5 },{ x: 14, y: 5 }];this.player = new Player(this, playerStartPosition[this.game.level].x, playerStartPosition[this.game.level].y, “player”)

Set Phasers to Re-Run

This is all well and good, but at the moment each of these sections only run once (except for our lovely friend the update of course) so how do we get our scene to reload for each level? And how do we let our array know which indices it should be looking at? All we need to do is create a function that runs when we exit the level. There are multiple ways to do this, but one way is to create an invisible zone (our ‘exit’) that when a player reaches it runs our exit function. Thankfully, Phaser has a game object called Zone and an overlap function that we can have run when a player entity meets the exit.

this.exit = this.physics.add.group({classType: Phaser.GameObjects.Zone});this.exit.create( 32, //x position 32, //y position 32, //height,
32 //width);
this.physics.add.overlap(this.player, this.exit,
this.exitLevel, //our exit function null, this);

So what are some things we need our exit function to do? One thing we need it to do is tell our game to move on to the next index of our array. You may have noticed in some places in the code above we are passing a variable called this.game.level as our indices position. This is how we can tell our game when to move to the next indices in the array! To start, in the constructor of this scene we can make a global variable called this.game.level and set it equal to 0. The reason we set it as this.game.level instead of this.level is so that it is accessible to all our scenes, just in case we want to use it in other scenes as well. And the reason we set it to 0 instead of 1 is because the first index of an array is zero. Now when a player touches an exit, we want our level to increase by 1 so that we are on to the next index of our array. So, in our exit function we will put:

this.game.level++

Ok, but we still need our game to reload. Thankfully Phaser 3 had a function for that too! All we need to put inside our function is:

this.scene.restart()

And boom! All three steps of our scene (the preload, the create, and the update) run again, but now it is being passed our information from index 1of our arrays! Yay! One important note to keep in mind though, is that Phaser 3 likes to help us out by having some storage it calls the cache where it stores information we have already preloaded such as audio. So instead of looking at our preload, it will just grab the information from the cache for us. Very handy if we are doing the same music each time, not so handy if we want it to check the preload for a new source. This is an easy fix though. All we have to do is remove the items from the cache in our exit function before we call the restart. For example:

this.music.destroy();this.cache.audio.remove("background")

It’s a good idea to console log this.cache to see what assets may be hiding in there and to remove them from the cache before running restart. When in doubt, console log!

And now we are done! We have some nice reusable code and we can spend more time writing new fun code instead of rewriting the same code for each level. (Stay DRY my friends)

Happy Coding!

--

--