JavaScript Game Programming Using Phaser

Emre Guneyler
Share

Phaser is a HTML5 game framework for desktop and mobile. It’s fast, free, and open source. Phaser is currently in version 2.0.7. It supports both WebGL and Canvas. It has a bunch of features to help you in your game development. It is like the Flixel game framework for ActionScript 3. In this article we will build a game skeleton with Phaser, making use of Phaser.State. For simplicity we won’t use any Phaser game objects like sprites or groups. I will mention a little bit about tweens as well.

Installing Phaser

Phaser is available through bower using the following command:

bower install phaser-official --save

Alternatively, you can grab the files directly from GitHub. The full version of Phaser is in the build directory. There are also customized builds, such as Phaser without the physics engine, located in the build/custom directory.

There are many community tutorials and getting started guides. I suggest you check out some of them in order to get familiar with Phaser. One of my favorites is a four part series on Flappy Bird. An extensive collection of tutorials, examples, and documentation is available on the official Phaser website.

Scaffolding Phaser

A Grunt-based Yeoman generator for Phaser is available for scaffolding projects. You can install it using the following commands:

npm install -g generator-phaser-official
yo phaser-official

This generator is great for getting up and running quickly, however, for the purposes of this tutorial, we will start from scratch.

Phaser Directory Structure

Our Phaser project will use the directory structure shown below. main.js starts the game, while app.js defines our Phaser application. The prefabs directory is for your game objects, while the states directory is for your game states.

|-- app.js
|-- main.js
|-- prefabs
`-- states
    |-- boot.js
    |-- level_intro.js
    |-- level_master.js
    |-- level_round.js
    |-- main_intro.js
    |-- main_menu.js
    `-- preload.js

You have to pay attention to the order you include these files if you are including them with plain script tags. I prefer using RequireJS, another alternative is Browserify.

Phaser.State

This section will focus on the use of Phaser.State. Phaser state encapsulates the different states of your game. Examples of game states are preload, main menu, level 1, level 2, help, pause, etc. When a state starts, you create the game objects relevant to that state. Later, you can switch to a different state, and Phaser will clean up your old game objects so you can create new game objects and display them.

You define a state by defining an object with some hook methods. The important ones are:

  • init – A method called when a state starts. It is passed an argument to enable sharing data between states.
  • preload – A method called when a state starts. It is used for loading assets before anything else.
  • create – A method called after preload, used for creating game objects.
  • update – A method called for every frame, which is used for user polling and collision detection.
  • shutdown – A method called when a state is shutdown, which is used for cleaning up game objects.

Organizing State Flow

This is a state chart for our Phaser states. The Boot and Preload states are the defacto states for setting up configuration and loading the game assets. The MainMenu state is for displaying the main menu. Other level states are for actual game play and switching between different levels and rounds. The levels are split into rounds. Each level has some rounds, which once played, allow you to move to the next level.

Phaser Game States

Game States

The Boot state loads preloader assets in the preload hook method and sets up Phaser game settings like scaling and input pointers via the create method.

File: states/boot.js

function Boot() {};

Boot.prototype = {
  preload: function() {
    // load preloader assets
  },
  create: function() {
    // setup game environment
    // scale, input etc..

    this.game.state.start('preload');
  }
};

The Preload state loads all the game assets, then switches to the main-intro state.

File: states/preload.js

Preload.prototype = {
  preload: function() {
    // load all game assets
    // images, spritesheets, atlases, audio etc..
  },
  create: function() {
    this.game.state.start('main-intro');
  }
};

The MainIntro state displays the game intro, logos, credits, etc. It doesn’t need a preload method, as it tweens an object for two seconds then switches to the main-menu state. I added a tween here, just to give you an idea that, you can tween your objects using effects like fading, shrinking, and sliding for state transition effects.

File: states/main_intro.js

function MainIntroState() {};

MainIntroState.prototype = {
  create: function() {
    // add main intro assets into the world
    this.tweenFadeState();
  },

  tweenFadeState: function() {
    this.game.add.tween({})
      .to({alpha: 1}, 2000)
      .onComplete.add(function() {
        this.game.state.start('main-menu');
      }, this);
  }
};

The MainMenu state displays the main menu. The user can then interact with items from the menu. For simplicity, I added a single keyboard event that will trigger a chain of tweens, and at the end switches to the level-master state. Chaining tweens is useful for combined animations like shrinking the menu and then fading the display.

File: states/main_menu.js

MainMenuState.prototype = {
  create: function() {
    this.enterKey = this.game.input.keyboard
        .addKey(Phaser.Keyboard.ENTER);

    this.enterKey.onDown.add(this.tweenPlayState, this);
  },
  tweenPlayState: function() {
    var tweenMenuShrink = this.game.add.tween({})
          .to({x: 0, y: 0}, 200);

    var tweenFadeIn = this.game.add.tween({})
          .to({alpha: 1}, 2000);

    tweenFadeIn.onComplete.add(function() {
      this.game.state.start('level-master');
    }, this);

    tweenMenuShrink.chain(tweenFadeIn);
    tweenMenuShrink.start();
  }
};

Notice in the sample I don’t tween anything useful. For simplicity, you should insert your game objects there. Also, I don’t create any game objects, but you can do that in the create method. For more information check out the Phaser examples and docs.

The LevelMaster state is a headless state that decides which state to switch to. It doesn’t display anything on the game world. It’s sole purpose is to decide whether it should switch to a level-round state or level-intro state, and most important, it updates and passes game data (this.levelData) between states.

File: states/level_master.js

LevelMasterState.prototype = {
  init: function(levelData) {
    if (!levelData) {
      levelData = {
        level: 0,
        round: 1,
        players: [
          { score: 0, skill: 1 },
          { score: 0, skill: 1 }
        ]
      };
    }

    this.levelData = levelData;
    this.winScore = 2;
  },

  create: function() {
    this.decideLevelState();
  }
};

When a new level starts, the level-intro state starts. The level-intro state displays an intro to the new level, like showing which level you are on. After the level-intro, it switches to level-round, which is where actual game play happens.

After a round finishes, it is either a new level-round or a new level. This logic happens in our decideLevelState function. If it’s the first level, or we have a winner for a level, we switch to the next level, otherwise we switch to the next round.

this.levelData holds the game data, such as the game level, game round, and player scores. We update it in our logic and pass along the states.

File: states/level_master.js

LevelMasterState.prototype = {
  decideLevelState: function() {
    if (this.isFirstLevel() || this.getWinningPlayer() !== -1) {
      this.nextLevel();
    } else {
      this.nextRound();
    }
  },
  nextLevel: function() {
    this.levelData.level++;

    this.levelData.players.forEach(function(p) {
      p.score = 0;
    }, this);

    this.levelData.round = 1;

    this.game.state.start('level-intro', true, false, this.levelData);
  },
  nextRound: function() {
      this.levelData.round++;
      this.game.state.start('level-round', true, false, this.levelData);
  }
};

The LevelIntro state displays level introduction information, such as which level you are on, and some intro animation. We pass the levelData parameter, that holds the game data. In the create method, we use levelData by displaying a skill menu if it’s the first level of the game. By skill menu I mean a menu that player selects which skill they want to play, but it’s up to you. At the end it switches to level-round state.

File: states/level_intro.js

LevelIntroState.prototype = {
  init: function(levelData) {
    this.levelData = levelData;
  },
  create: function() {
    var tweenIntro = this.tweenIntro();

    if (this.levelData.level === 1) {
      var tweenSkillMenuPop = this.tweenSkillMenuPop();

      tweenIntro.chain(tweenSkillMenuPop);
      tweenSkillMenuPop.onComplete.add(this.levelStart, this);
    } else {
      tweenIntro.onComplete.add(this.levelStart, this);
    }
  },
  levelStart: function() {
    this.game.state.start('level-round', true, false, this.levelData);
  },
  tweenIntro: function() {
    var tween = this.game.add.tween({})
      .to({alpha: 0}, 1000, Phaser.Easing.Linear.None, true);

    return tween;
  },
  tweenSkillMenuPop: function() {
    var tween = this.game.add.tween({})
      .to({x: 1, y: 1}, 500, Phaser.Easing.Linear.None, true);

    return tween;
  }
};

Finally, the LevelRound state is where the actual game play happens. You can use its update method if you need. For simplicity, I added a simple key interaction that ends the state when Enter is pressed. The important note here is that, it switches back to level-master, passing along the levelData that it got from level-master in the first place.

File: states/level_round.js

LevelRoundState.prototype = {
  init: function(levelData) {
    this.levelData = levelData;
  },
  create: function() {
    this.enterKey = this.game.input.keyboard
      .addKey(Phaser.Keyboard.ENTER);

    this.enterKey.onDown.add(this.roundEnd, this);
  },
  roundEnd: function() {
    this.nextRound();
  },
  nextRound: function() {
    this.game.state.start('level-master', true, false, this.levelData);
  }
};

This completes our state flow. All this provides us a level state loop, that looks something like this:

Boot -> Preload ->
main-intro -> main-menu ->
level-master -> Level1 ->
level-master -> L1 Round1 ->
level-master -> L1 Round2 ->
level-master -> Level2 ->
level-master -> L2 Round1 ->

You can exit this loop in your level-round state by an action that switches to main-menu state.

Startup Phaser

Now, we will start the Phaser game. Place this div inside your page. Phaser will place it’s canvas there.

File index.html

<div id="game-area"></div>

We have to create a Phaser.Game, add all our states to the StateManager, and start the boot state.

File: app.js

function Game() {}

Game.prototype = {
  start: function() {
    var game = new Phaser.Game(640, 480, Phaser.AUTO, 'game-area');

    game.state.add('boot', BootState);
    game.state.add('preload', PreloadState);
    game.state.add('main-intro', MainIntroState);
    game.state.add('main-menu', MainMenuState);
    game.state.add('level-master', LevelMasterState);
    game.state.add('level-intro', MainIntroState);
    game.state.add('level-round', LevelRoundState);
    game.state.start('boot');
  }
};

Finally, fire up the game using the following code.

File: main.js

var game = new Game();
game.start();

Conclusion

That concludes our Phaser.State article. This is just the skeleton of a Phaser game. There are lots of other things Phaser provides you, like sprites, animations, sounds, physics, scaling, and more. You can find the repo for our example on GitHub.