Using Phaser

to build a music game

BUT FIRST,

LET ME TAKE YOU THROUGH A DEMO*

* The demo was localhost only, you can play the full game here.

About Me

My name is Osman Zeki. Oz, if you want. :)

I work at Turbulent as a Frontend Developer.

Webdev is my day job, sometimes gamedev too.

The Team

Art Direction

Music Composition

Game Design

Project Management

Programming

oz

The Project

  • JAM is a TV show about young Franco-Ontarian musicians
    on a journey towards Montreal's Francofolies festival.
  • Runs on the TFO channel and mosty targets a teenage audience.
  • Turbulent handles design and tech (web and game).

Fair-Play

Elevator Pitch

  • Arcade music runner (Highscore + Music creation).
  • 8 unique music genres / characters / endings.
  • 5 instrument loops per genre + 3 energy variations.
  • 4 time of day variations for backdrops and scenery.
  • Leaderboards + contest and prizes.
  • User profiles + level music playback + social shares.
  • Needs to run on Desktop and Tablets. In the browser.

We've got three months before the next season begins.

No time to lose!

Challenges Ahead

  • Mobile: Only one sound source at a time.
  • Mobile: Sound can only be activated by user input.
  • WebGL is fast but absent in mobile. Has to be Canvas.
  • Web Audio API seems powerful but very complex.
  • Website features will require a lot of my dev time.
  • Good media loading strategy is dearly needed.
  • Need to be able to manage lots of game entities.
  • Will probably need some sort of level editor.

Solutions

AudioSprite technique solves single source playback limitation on mobile.

First-ever user input triggers eternal silent sound loop that allows for game-logic triggered sound from now on.

Unity and Haxe are considered but ruled out because various constraints don't offer favorable scenario. No free lunch.

JSBreakouts.org provides entertaining variant of the classic TodoMVC demos with game engines instead of JS Frameworks + classic Breakouts game instead of TodoApp. Quality time is spent "studying" engines here. :)

aka "But how ?!"

Phaser 2.0.5

Pixi.js rendering = Fast.

Flixel inspired API = Proven Designs.

Phaser features = Batteries included.

Supports AudioSprite and touch sound unlocking.

Automatic switch between Canvas and WebGL without any special changes to the game logic. Best of both worlds!

Provides many game programmin' necessities such as:

  • Event Messaging (Signals)
  • Groups / SceneGraph
  • Sound
  • State Management
  • Scale Management
  • Physics
  • Animation

Open-source. MIT License. Cleanest source code I've ever read.

(latest at the time, now 2.4.2)

Code Architecture

A clever StateMachine pattern allows for different code paths to be executed depending on the active game state.

// Boot State
JamGame.Boot = function () {
  Phaser.State.call(this);
  // this.game ...
};

JamGame.Boot.prototype = {
  preload:  function () {},
  create:   function () {},
  update:   function () {},
  render:   function () {},
  paused:   function () {},
  shutdown: function () {}
};
// Game State
JamGame.Game = function () {
  Phaser.State.call(this);
  // this.game ...
};

JamGame.Game.prototype = {
  preload:  function () {},
  create:   function () {},
  update:   function () {},
  render:   function () {},
  paused:   function () {},
  shutdown: function () {}
};

STATE MANAGEMENT

A game state is basically just a simple Javascript Object implementing the Phaser.State interface.

Code Architecture

You start things up by instantiating Phaser, Declaratively adding your states to the engine and Starting the initial game state.

// Instantiate Phaser game engine
JamGameInstance = new Phaser.Game(
  960,                // Width
  640,                // Height
  Phaser.AUTO,        // Render mode AUTO, CANVAS, WEBGL
  'canvas-container'  // DOM Container
);

// Declare game states
JamGameInstance.state.add('Boot',     JamGame.Boot);
JamGameInstance.state.add('Preload',  JamGame.Preload);
JamGameInstance.state.add('MainMenu', JamGame.MainMenu);
JamGameInstance.state.add('Game',     JamGame.Game);

// Enter Boot state
JamGameInstance.state.start('Boot');

Initialization

Code Architecture

An interesting API wraps engine Objects in Factories. This allows the engine to control instantiation (diminish Garbage Collection) and implicitly keep track of all game entities in the World, pooling, etc.

JamGame.LevelEnding.prototype.create = function ()
{
  // Getting objects via Factories seems like a good
  // way of respecting the "Javascript Way" of doing
  // initialization. In a way, Javascript is natively
  // using this technique with Object.create().

  var fan_group   = game.add.group();
  var music_stage = game.add.sprite(50, 300, 'music_stage');
  var fans        = game.add.image(50, 300, 'fans');
  
  fan_group.add(fans);
};

Factories

Engine Features

Arcade : Fast AABB physics and math helpers (distance, angle, etc.)

P2         : Full-body physics, springs, etc.

Ninja     : Complex tiles, slopes, etc.

Box2D  : Available as a paid plugin.

JamGame.Level.prototype.create = function ()
{
  // Enable Arcade Physics
  game.physics.startSystem(Phaser.Physics.ARCADE);
    
  // Player & Obstacle
  player   = game.add.sprite(400, 300, 'player');
  obstacle = game.add.sprite(400, 300, 'obstacle');

  // Enable collisions
  game.physics.enable(player,   Phaser.Physics.ARCADE);
  game.physics.enable(obstacle, Phaser.Physics.ARCADE);
};

JamGame.Level.prototype.update = function ()
{
  // Check for collisions
  game.physics.arcade.collide(player , obstacle , collisionHandler, null, this);
};

JamGame.Level.prototype.collisionHandler= function (collider1, collider2)
{
  console.log (collider1.name + " has collided with " + collider2.name);

  // You could also send a Signal from here.
};

Physics

Engine Features

Unified event system allows complex game behaviours without explicit coupling between game entities. Phaser uses Signals everywhere internally. Also provides Phaser.Signal for custom events.

JamGame.Player.prototype.preload = function ()
{
  // Custom event
  events.onTriggerCustom = new Phaser.Signal();
  
  // Example event trigger
  events.onInputDown.add(this.triggerCustomSignal, this);
};

JamGame.Player.prototype.triggerCustomSignal = function ()
{
  // Dispatch custom event, anyone listening
  // will be able to react accordingly.
  events.onTriggerCustom.dispatch(this);
};

Signals

Engine Features

Greatly simplifies Web Audio API usage for games . Native support for AudioSprite and Sound Unlocking techniques on mobile devices is a miracle. Still had to build custom features on top Phaser.Sound and Phaser.SoundManager to allow for complex music synchronization.

JamGame.Level.prototype.preload = function ()
{
  // Load the instruments AudioSprite, it contains
  // marker information to identify sounds in a single
  // continous source file.
  game.load.audiosprite(
    'instruments',
    'assets/audio/electro/instruments/audiosprite.ogg',
    null,
    instrumentsJSON
  );
};

JamGame.Level.prototype.create = function ()
{
  // Create an AudioSprite instance
  instruments = game.add.audioSprite('instruments');
};

JamGame.Level.prototype.onTrigger = function (instrument)
{
  instruments.play(instrument.name);
};

Sound

Additional Ressources

Phaser Sandbox provides JSFiddle like playground.

Searchable Docs is interesting to familiarize self with API.

Code examples provide simple grasp on engine features.

Official and Community tutorials fill the gaps that examples and source code sometimes don't.

Amazing community provides help and opportunities to exchange and learn and become better at game making.

Aftermath

Total Lines of Code in Phaser :        77 727 LOC (non-minified)

Total cups of coffee consumed :      Number.MAX_VALUE

Total Lines of Code in JAM :             12 169 LOC (non-minified)

  • Game was nominated in the Youth Convergence Production at the 2015 Numix gala. Sadly did not win.
  • Launched with a contest that allowed best players to compete to win a free guitar + amplifier.
  • Two whole new levels will be added by the end of summer 2015: ElectroJazz and Classical.
  • A few bugs were found in Phaser during development. Reported them and @photostorm was super receptive! In the end we gave something back to the community.

Questions ?

osman@zeki.ca

Contact

Using Phaser to Build a Music Game

By Osman Zeki

Using Phaser to Build a Music Game

In this talk I discuss how we used Phaser to develop a browser-based HTML5 Arcade/Music game that had to run on desktops and tablets. I'll describe the cross-browser compatibility, audio-visual synchronization and performance challenges we faced and give examples of how the engine's many features were used.

  • 790