DATA FLOW IN PHASER.IO
GETTING TO KNOW PHASER
Although I haven’t been a lifelong computer programmer, I’ve loved videogames ever since my little brother and I convinced our very stern Soviet father to buy us an N64 on Ebay. We basically had to write a treatise on why games aren’t just TV with controllers and actually exercise your brain in certain ways. Yes, this treatise was created for the sake of a game console, but I stick by it to this day, and it was my love of strange, beautiful games that got me into coding (I wanted to make my own). The first game-building framework I messed with was Phaser.io, which was built using HTML5 and can help you build really cool stuff using Javascript (which I know) or Typescript (which I don’t…yet).
I don’t want to bother trying to write a great well-rounded introduction to Phaser.js, because that has already been done in this clever, funny Medium article by Jerra Haynes. Read that, and then come back here so I can fill in some blanks on information flow for you.
There are, in fact, a lot of docs and tutorials you can use to learn the basics (including this one from Phaser’s own website, which teaches you how to make a basic 2D platformer, is very readable, and which I highly recommend to any programmer who wants to make games). But when I tried to go beyond the single-scene, single-HTML file layout of Phaser’s little intro tutorial, I had a lot of trouble sussing out how to pass information between scenes. The Phaser docs can be rather inscrutable; this meant a lot of digging on sites like StackOverflow.
So far, I’ve found three key ways to do share and access information: the init function, the base scene, and the leveraging of class structure. The following guide assumes that you are using ES-6 class construction to build your game: every game played by the user is an instance of the Game class, every scene is an instance of a certain scene class, even entities like the player can be instances of a Player class. This means you’ll have a more complex file schema in your project folder, but your individual files will be divided neatly into class-specific, DRY code. Anyone who’s ever worked with React.js should recognize this method of organization and extension. Files using Phaser look very similar to files using React!
THE INIT() FUNCTION
Phaser’s tutorial teaches you that all Phaser scenes have three magic functions: preload, create, and update. These functions are where you bring in your assets — images, spritesheets for animation, backgrounds, sound effects, music — and where you write the rules of the scene — how things move, how things collide, how scores add up. Up till this point, I was doing fine and having a lovely time making my own Piskel art. But when it came time to hook my Main Game scene to my Game Over scene, I was pulling my hair out over the simple act of passing along the player’s final score from one scene to another.
It took a lot of googling to find out there is a fourth magic function in Phaser scenes: init! I don’t know why the tutorial skips over this gem, because it’s super-valuable for any game that has more than one scene in it. The init function runs each time a scene is initialized and accepts one argument: the data object. Any key-value pairs you attach to this data object can be accessed by the scene it’s passed to. Add your init function right after the constructor and before preload, because it happens at the very beginning of the scene (when it initializes — hence the name).
To solve my problem, all I had to do was pass a data object with a score property as the second argument to my start-scene method in MainScene.js:
this.scene.start(“GameOverScene”, {score: this.score});
And then I grab the final score from the init function in GameOverScene.js and attach it handily to the scene’s state:
init(data) {
this.finalScore = data.score;
}
After that, it’s easy to render the data using Phaser’s add-text functionality. Just a few lines of code can make this crucial connection within your game! You can pass all kinds of information this way — scores, collected items, Boolean flags. The data object could have one key on it or a hundred, it’s really up to you.
THE BASE SCENE
But then, what if you build a more complex game with lots of information shared between scenes? What if you want to share the same central assets, methods, or collisions between scenes? Often, games are composed of levels that share lots of the same assets, backgrounds, sprite velocities, or collisions. In this case, you can create a base (parent) scene that contains methods and properties used in multiple scenes, then build your sub-scenes as extensions of the base scene.
Anyone who’s worked with frameworks like React.js or just plain old vanilla JavaScript class construction is familiar with creating parent classes with general, widely-used properties and methods, then extending these to build more specific sub-classes. You can use class construction to build every part of your Phaser game!
Your base scene file could look something like this:
And then your Scene X could look something like this:
One important thing to note here is the use of the preload/create/update functions when extending scenes off each other. If you define a create function in BaseScene.js and then extend Scene1 from BaseScene, Scene1 will already have this.create attached as a method. You can’t have two “create” methods on the same object; if you forget this, and write a new create function in Scene1.js, the two create functions will not merge! Scene1’s create will override the one defined in BaseScene.js and important information could be lost. Same goes for update.
In my experience, it’s best to preload in the base scene and avoid using create or update. Write your own unique gameplay methods on the base scene’s prototype; then access any of these whenever you want in the create/update functions of Scene X with this.someMethod() or this.someProperty. In my example, the enemyCollision method is defined in the base scene, then called in Scene1 as this.enemyCollision. This will save you from preloading the same backgrounds and sprites and music all over the place.
And this brings us to our final point…
LEVERAGING CLASS STRUCTURE
Making everything in your game an instance of a class makes it easy to hold onto important information. You can “this.anything-you-want!” Just remember, though, that if you want to access a property or method that’s attached to a scene, you need to be aware of what scope you’re in. If you’re still inside the scene, just attach the property or method to “this”.
But if you are, say, trying to invoke a method called levelUp, which you defined in the Scene1 class, but now you’re inside the Player class, “this” is no longer that scene. In the Player class, there is no “this.levelUp.” How do you access that method?
My first instinct would be to call this.scene.levelUp. But “this.scene” doesn’t exist either — after all, how does the Player Class know if it’s in SceneX or SceneY? First, in the scene where you’re creating a new instance of the Player, pass it this (which points to the scene itself).
this.player = new Player(this);
Then you can pass a sceneName argument to the constructor and super of the Player Class.
export default class Player extends Phaser.Physics.Arcade.Sprite {
constructor(sceneName) {
super(sceneName);
this.scene = sceneName;
}
}
Now the scene where the method was defined is attached to the instance of Player as this.scene, and invoking “this.scene.levelUp()” will work! Class structure is very handy, but it’s easy to get tangled up if you don’t remember the shifting context of “this” and keep your scopes straight.
RIDING OFF INTO THE SUNSET
There is so much to learn with Phaser! There are different Physics engines, different things you can do with your “camera”, different technologies you can tie into your game…both of the games I’ve worked on have been 2D, side-view games using the Arcade physics engine. I’ve learned so much just from these attempts, but this is dwarfed by the amount of things I’ve yet to learn. My advice is to look things up as best you can. I had to read so many Stack-Overflows before striking upon init. Googling is an art, so choose your search keywords very carefully, and make sure you’re not using docs that are actually for Phaser 2 or CE. Treat every new project like a learning experience, because once you figure out how to do one simple thing in Phaser, you can use it again and again, and it opens the path to many other potential magic tricks. Learn how to make the kind of games you wished existed before you knew how to make them yourself!
P.P.S. I still play that N64!