Osman Zeki
Interactive Developer in Montréal, Canada
BUT FIRST,
LET ME TAKE YOU THROUGH A DEMO*
* The demo was localhost only, you can play the full game here.
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.
Art Direction
Music Composition
Game Design
Project Management
Programming
oz
Fair-Play
We've got three months before the next season begins.
No time to lose!
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. :)
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:
(latest at the time, now 2.4.2)
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 () {}
};A game state is basically just a simple Javascript Object implementing the Phaser.State interface.
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');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);
};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.
};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);
};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);
};Phaser Sandbox provides JSFiddle like playground.
Searchable Docs is interesting to familiarize self with API.
Amazing community provides help and opportunities to exchange and learn and become better at game making.
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)
osman@zeki.ca
By Osman Zeki
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.