Adventures in Phaser with TypeScript– Where did my game loop go?

 

 

One thing every single game has in common is a game loop.  That said, it’s not always under your control!  Today we are going to look at how the game loop is implemented in the Phaser HTML5 game engine.

 

Pretty much every single video game ever created follows the same basic program flow:

 

Program Starts

Check Input

Update World

Draw scene

Program Exits

 

Of course this is a massive simplification ignoring things like updating physics simulations, multiple threads, streaming of assets, etc… but the basic flow is there in every game.  The three indented process, Input, Update and Draw are performed over and over in a loop, thus “game loop”.  In Phaser there is no exception, but the way it’s handled is a bit different.

 

If you’ve done any JavaScript game development before you’ve no doubt encountered requestAnimationFrame or if using an older browser setTimeout JavaScript functions.  Both perform basically the same task, they call a function once an interval, such as every 30th of a second if your game is set to run at 30fps.  This is the very heart of most JavaScript games and Phaser is no exception.  You the end developer don’t have to care about such low level aspacts though as this functionality is taken care of in the class Phaser.RequestAnimationFrame and is automatically created by Phaser.Game.  If you want to see the actual game loop driving your game though, I suppose this code snippet from RequestAnimationFrame.js is it:

 

    updateRAF: function () {            this.game.update(Date.now());            this._timeOutID = window.requestAnimationFrame(this._onLoop);        }

 

As you can see, its basically just calling Game’s update() over and over.  Now if we take a look at the source code for update in Game it all becomes clear:

 

update: function (time) {        this.time.update(time);        if (!this._paused && !this.pendingStep) {          if (this.stepping) {              this.pendingStep = true;          }            this.debug.preUpdate();          this.physics.preUpdate();          this.state.preUpdate();          this.plugins.preUpdate();          this.stage.preUpdate();            this.state.update();          this.stage.update();          this.tweens.update();          this.sound.update();          this.input.update();          this.physics.update();          this.particles.update();          this.plugins.update();            this.stage.postUpdate();          this.plugins.postUpdate();      }      else {          this.state.pauseUpdate();          // this.input.update();          this.debug.preUpdate();      }        if (this.renderType != Phaser.HEADLESS) {          this.renderer.render(this.stage);          this.plugins.render();          this.state.render();          this.plugins.postRender();      }    }

 

So there is your traditional game loop, just nicely tucked away.  So then, where then does your code fit in all of this?  Remember back in the Hello World post when we created a Game instance we past in a “State” object implementing create and passed in the function this.create to be called, like so:

this.game = new Phaser.Game(800, 600, Phaser.AUTO, 'content', { create: this.create });

 

Well, that’s how we do it.  A State object has a number of functions that will be called, in this case we provide an implementation for create, now lets look at a slightly more complicated example:

 

class SimpleGame {        constructor() {          this.game = new Phaser.Game(800, 600, Phaser.CANVAS, 'content', {              create: this.create, update: this.update,          render: this.render});      }        game: Phaser.Game;      textValue: Phaser.Text;      updateCount: number;        create() {          var style = { font: "65px Arial", fill: "#ff0000", align: "center" };          this.textValue = this.game.add.text(0, 0, "0", style);          this.updateCount = 0;      }        update() {          this.textValue.text = (this.updateCount++).toString();      }        render() {          this.game.debug.text("This is drawn in render()", 0, 80);      }  }    window.onload = () => {      var game = new SimpleGame();  };

 

Here is the code running:

 

 

In this example the State object we are passing in to the Phaser.Game constructor implements create, update and render.  Create will be called once, predictably enough on creation.  Here we create a red text object like we did in the Hello World example.  This time however we keep a reference to it.  We also add a counter variable updateCount.  Each frame update() will be called, we simply increment the counter value and assign this to out text object.  Finally in render we draw our text using game.debug.text().  Phaser provides a number of convenient debug methods for dumping information on screen, either as text or graphically.  These functions however are *NOT* optimized and should not be used in production!

 

So as you can see, update works pretty much like you would expect, but instead of your game controlling the loop you implement methods in a State object that will be called by the Phaser engine.

 

Let’s look at a slightly more complex example that will probably make State objects make a bit more sense.  This is a two screen game, first there is a title sreen shown that when clicked then moves to the game state, which is the same as the above demo.  Let’s jump in with code:

 

module GameFromScratch {      export class TitleScreenState extends Phaser.State {          game: Phaser.Game;          constructor() {              super();          }          titleScreenImage: Phaser.Sprite;            preload() {              this.load.image("title", "TitleScreen.png");          }          create() {              this.titleScreenImage = this.add.sprite(0, 0, "title");              this.input.onTap.addOnce(this.titleClicked,this); // <-- that um, this is extremely important          }          titleClicked (){              this.game.state.start("GameRunningState");          }      }        export class GameRunningState extends Phaser.State {          constructor() {              super();          }          textValue: Phaser.Text;          updateCount: number;            create() {              var style = { font: "65px Arial", fill: "#ff0000", align: "center" };              this.textValue = this.game.add.text(0, 0, "0", style);              this.updateCount = 0;          }            update() {              this.textValue.text = (this.updateCount++).toString();          }            render() {              this.game.debug.text("This is drawn in render()", 0, 80);          }      }        export class SimpleGame {          game: Phaser.Game;            constructor() {              this.game = new Phaser.Game(800, 600, Phaser.WEBGL, 'content');                this.game.state.add("GameRunningState", GameRunningState, false);              this.game.state.add("TitleScreenState", TitleScreenState, false);              this.game.state.start("TitleScreenState", true, true);          }        }  }    window.onload = () => {      var game = new GameFromScratch.SimpleGame();  };

 

And when you run it you see ( click to proceed ):

 

One thing to be aware of right away is this example should probably be split across 3 files not in a single one.  I kept them together to make following along easier.

 

Here, instead of creating a state object inline we declare two of them.  Here we are using the TypeScript ability extend to create Phaser.State derived objects as inheritance really isn’t part of JavaScript. Let’s take a quick look at what this code does, starting with SimpleGame.

 

Here instead of providing a State object to the Phaser.Game constructor inline ( in { } form ) we register 2 different states using game.state.add().  The first value is the string value we will access this state using, the second is the state itself while the final value is if you want to start the state while adding it.  Finally after adding each state we start one calling game.stat.start and pass in the key value of the state.  Yes, we could have simply added true when we added the TitleScreenState, but I think doing it long form is clearer.

 

Once our TitleScreenState starts, in preload it loads an image, then in create it adds the image as a sprite.  Finally it adds a tap handler that will be called when the screen is tapped.  Image loading, sprites and input will all be covered later.  One very important thing to understand here though is the this parameter passed in to onTap.addOnce.  The second value is the context that titleClicked will be called in.  In other words, the value of “this” within titleClicked is determined by the value you pass here.  This is one of the warts of JavaScript IMHO and I wished TypeScript fixed it, although it appears it hasn’t.  The importance here is, if you don’t pass the context into the Signal (onTap) then the called function (titleClicked) wont have access to itself!  You will then get an error that this.game is undefined.  Finally when titleClicked is called we launch the GameRunningState like we did earlier.  GameRunningState is basically just the functionality from our earlier example split out into Phaser.State form.

 

As you can see, Phaser.State objects allow you to logically split up your game functionality.  Of course you could just implement a single inline state like we did earlier and ignore them from this point on.

 

Programming


Scroll to Top