Matthew Wilber
Full-Stack Web Developer. Building web sites & mobile apps for fun and profit! Free-time freelancer and UI/UX Engineer @ChartIQ
greenzeta.com
Combat 2077
Flash 4
Visionary Media 2000
Acme Agent Hunter
Flash 5
Warner Bros. 2003
Go Turtle Go!
Flash 8
Little Tikes 2011
Peanut Butter Art
EaselJS
Hormel 2015
Smashion
Phaser
VFiles 2016
Puppy Club
Phaser / Cordova
Mini Trading Cards Co. 2018
...the short version
First, there was Flash. And it was good...
But it required a plug-in.
Then HTML5 gave us <canvas>,
which had everything...
Except an easy way to use it.
This led to the creation of, many,
specialized frameworks.
Open Source HTML <canvas> game development framework that's "fast fun and free"
ECMA Script 2015 (ES6)
Webpack & Babel
Node Package Manager (npm)
Start with the GreenZeta Webpack Boilerplate
import 'phaser';
src/main.js
npm install --save phaser
CLI
github.com/mwilber/zeta-bros > Step1
import { GameScene } from './scenes/GameScene'; const gameConfig = { type: Phaser.AUTO, width: 800, height: 600, parent: 'phaser-game', dom: { createContainer: true }, physics: { default: 'arcade', arcade: { gravity: { y: 900 }, debug: false } }, scene: [ GameScene ] }; new Phaser.Game(gameConfig);
src/main.js
github.com/mwilber/zeta-bros > Step2
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); } create() { // Add the background image this.add.image(400, 300, 'background'); } update() { } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step3
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); } create() { // Add the background image this.add.image(400, 300, 'background'); this.player = this.createPlayer(); } update() { } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step4
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); } create() { // Add the background image this.add.image(400, 300, 'background'); this.cursors = this.input.keyboard.createCursorKeys(); this.player = this.createPlayer(); } update() { if (this.cursors.left.isDown){ this.player.setVelocityX(-200); }else if (this.cursors.right.isDown){ this.player.setVelocityX(200); }else{ this.player.setVelocityX(0); } if (this.cursors.up.isDown && this.player.body.touching.down){ this.player.setVelocityY(-750); } } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step5
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); } create() { // Add the background image this.add.image(400, 300, 'background'); this.initAnimation(); this.cursors = this.input.keyboard.createCursorKeys(); this.player = this.createPlayer(); } update() { if (this.cursors.left.isDown){ this.player.setVelocityX(-200); this.player.anims.play('left', true); }else if (this.cursors.right.isDown){ this.player.setVelocityX(200); this.player.anims.play('right', true); }else{ this.player.setVelocityX(0); this.player.anims.play('turn'); } if (this.cursors.up.isDown && this.player.body.touching.down){ this.player.setVelocityY(-750); } } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } initAnimation() { this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('zeta', { start: 0, end: 3 }), frameRate: 30, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'zeta', frame: 4 } ], frameRate: 30 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('zeta', { start: 5, end: 8 }), frameRate: 30, repeat: -1 }); } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step6
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); this.load.image('ground', 'assets/images/ground.png'); this.load.image('wall', 'assets/images/wall.png'); this.load.image('platform', 'assets/images/platform.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); } create() { // Add the background image this.add.image(400, 300, 'background'); this.initAnimation(); this.cursors = this.input.keyboard.createCursorKeys(); this.platforms = this.createPlatforms(); this.player = this.createPlayer(); } update() { if (this.cursors.left.isDown){ this.player.setVelocityX(-200); this.player.anims.play('left', true); }else if (this.cursors.right.isDown){ this.player.setVelocityX(200); this.player.anims.play('right', true); }else{ this.player.setVelocityX(0); this.player.anims.play('turn'); } if (this.cursors.up.isDown && this.player.body.touching.down){ this.player.setVelocityY(-750); } } createPlatforms() { let platforms = this.physics.add.staticGroup(); platforms.create(400, 270, 'platform'); platforms.create(400, 572, 'ground'); return platforms; } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } initAnimation() { this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('zeta', { start: 0, end: 3 }), frameRate: 30, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'zeta', frame: 4 } ], frameRate: 30 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('zeta', { start: 5, end: 8 }), frameRate: 30, repeat: -1 }); } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step7
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); this.load.image('ground', 'assets/images/ground.png'); this.load.image('wall', 'assets/images/wall.png'); this.load.image('platform', 'assets/images/platform.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); } create() { // Add the background image this.add.image(400, 300, 'background'); this.initAnimation(); this.cursors = this.input.keyboard.createCursorKeys(); this.platforms = this.createPlatforms(); this.player = this.createPlayer(); this.physics.add.collider(this.player, this.platforms); } update() { if (this.cursors.left.isDown){ this.player.setVelocityX(-200); this.player.anims.play('left', true); }else if (this.cursors.right.isDown){ this.player.setVelocityX(200); this.player.anims.play('right', true); }else{ this.player.setVelocityX(0); this.player.anims.play('turn'); } if (this.cursors.up.isDown && this.player.body.touching.down){ this.player.setVelocityY(-750); } } createPlatforms() { let platforms = this.physics.add.staticGroup(); platforms.create(400, 270, 'platform'); platforms.create(400, 572, 'ground'); return platforms; } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } initAnimation() { this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('zeta', { start: 0, end: 3 }), frameRate: 30, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'zeta', frame: 4 } ], frameRate: 30 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('zeta', { start: 5, end: 8 }), frameRate: 30, repeat: -1 }); } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step8
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); this.load.image('ground', 'assets/images/ground.png'); this.load.image('wall', 'assets/images/wall.png'); this.load.image('platform', 'assets/images/platform.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); this.load.image('bot', 'assets/images/security_bot.png') } create() { // Add the background image this.add.image(400, 300, 'background'); this.initAnimation(); this.cursors = this.input.keyboard.createCursorKeys(); this.platforms = this.createPlatforms(); this.player = this.createPlayer(); this.bots = this.physics.add.group(); this.physics.add.collider(this.player, this.platforms); this.physics.add.collider(this.bots, this.platforms); this.spawnBot(); } update() { if (this.cursors.left.isDown){ this.player.setVelocityX(-200); this.player.anims.play('left', true); }else if (this.cursors.right.isDown){ this.player.setVelocityX(200); this.player.anims.play('right', true); }else{ this.player.setVelocityX(0); this.player.anims.play('turn'); } if (this.cursors.up.isDown && this.player.body.touching.down){ this.player.setVelocityY(-750); } } createPlatforms() { let platforms = this.physics.add.staticGroup(); platforms.create(400, 270, 'platform'); platforms.create(400, 572, 'ground'); return platforms; } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } initAnimation() { this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('zeta', { start: 0, end: 3 }), frameRate: 30, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'zeta', frame: 4 } ], frameRate: 30 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('zeta', { start: 5, end: 8 }), frameRate: 30, repeat: -1 }); } spawnBot(){ this.bots.create(100, 75, 'bot').setVelocityX(100); } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step9
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); } preload() { this.load.image('background', 'assets/images/background.png'); this.load.image('ground', 'assets/images/ground.png'); this.load.image('wall', 'assets/images/wall.png'); this.load.image('platform', 'assets/images/platform.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); this.load.image('bot', 'assets/images/security_bot.png') } create() { // Add the background image this.add.image(400, 300, 'background'); this.initAnimation(); this.cursors = this.input.keyboard.createCursorKeys(); this.platforms = this.createPlatforms(); this.walls = this.createWalls(); this.player = this.createPlayer(); this.bots = this.physics.add.group(); this.physics.add.collider(this.player, this.platforms); this.physics.add.collider(this.bots, this.platforms); this.physics.add.collider(this.bots, this.walls, this.handleCollisionWall.bind(this)); this.physics.add.collider(this.player, this.bots, this.handleCollisionEnemy.bind(this)); this.spawnBot(); } update() { if (this.cursors.left.isDown){ this.player.setVelocityX(-200); this.player.anims.play('left', true); }else if (this.cursors.right.isDown){ this.player.setVelocityX(200); this.player.anims.play('right', true); }else{ this.player.setVelocityX(0); this.player.anims.play('turn'); } if (this.cursors.up.isDown && this.player.body.touching.down){ this.player.setVelocityY(-750); } } createPlatforms() { let platforms = this.physics.add.staticGroup(); platforms.create(400, 270, 'platform'); platforms.create(400, 572, 'ground'); return platforms; } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } createWalls() { let walls = this.physics.add.staticGroup(); walls.create(0, 300, 'wall').setActive(true); walls.create(800, 300, 'wall').setActive(true); return walls; } handleCollisionWall(event, collider){ if(collider.body.touching.left){ event.setVelocityX(-100); }else if(collider.body.touching.right){ event.setVelocityX(100); } return true; } handleCollisionEnemy(event, collider) { this.scene.start('EndScene'); } initAnimation() { this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('zeta', { start: 0, end: 3 }), frameRate: 30, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'zeta', frame: 4 } ], frameRate: 30 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('zeta', { start: 5, end: 8 }), frameRate: 30, repeat: -1 }); } spawnBot(){ this.bots.create(100, 75, 'bot').setVelocityX(100); } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step10
github.com/mwilber/zeta-bros > Step11
import 'phaser'; import { GameScene } from './scenes/GameScene'; import { EndScene } from './scenes/EndScene'; const gameConfig = { type: Phaser.AUTO, width: 800, height: 600, parent: 'phaser-game', dom: { createContainer: true }, physics: { default: 'arcade', arcade: { gravity: { y: 900 }, debug: false } }, scene: [ GameScene, EndScene ] }; new Phaser.Game(gameConfig);
src/main.js
import Phaser from 'phaser'; export class EndScene extends Phaser.Scene { constructor() { super({ key: 'EndScene' }); } preload() { this.load.image('end_splash', 'assets/images/end_splash.png'); } create() { // Add the background image this.splash = this.add.image(400, 300, 'end_splash'); this.splash.setInteractive().on('pointerdown', () => { this.scene.start('IntroScene'); }); } }
src/scenes/EndScene.js
import Phaser from 'phaser'; export class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); this.levelCt = 1; } preload() { this.load.image('background', 'assets/images/background.png'); this.load.image('ground', 'assets/images/ground.png'); this.load.image('wall', 'assets/images/wall.png'); this.load.image('platform', 'assets/images/platform.png'); this.load.spritesheet('zeta', 'assets/images/zeta_spritesheet.png', { frameWidth: 40, frameHeight: 60 } ); this.load.spritesheet('door', 'assets/images/door.png', { frameWidth: 64, frameHeight: 64 } ); this.load.image('bot', 'assets/images/security_bot.png') } create() { // Add the background image this.add.image(400, 300, 'background'); this.initAnimation(); this.cursors = this.input.keyboard.createCursorKeys(); this.door = this.createDoors(); this.doorsign = this.createDoorSign(); this.platforms = this.createPlatforms(); this.walls = this.createWalls(); this.player = this.createPlayer(); this.bots = this.physics.add.group(); this.physics.add.collider(this.player, this.platforms); this.physics.add.collider(this.bots, this.platforms); this.physics.add.collider(this.bots, this.walls, this.handleCollisionWall.bind(this)); this.physics.add.collider(this.player, this.bots, this.handleCollisionEnemy.bind(this)); this.physics.add.overlap(this.player, this.door, this.handleOverlapDoor.bind(this)); this.doorsign.setText(this.levelCt); this.spawnBot(); } update() { if (this.cursors.left.isDown){ this.player.setVelocityX(-200); this.player.anims.play('left', true); }else if (this.cursors.right.isDown){ this.player.setVelocityX(200); this.player.anims.play('right', true); }else{ this.player.setVelocityX(0); this.player.anims.play('turn'); } if (this.cursors.up.isDown && this.player.body.touching.down){ this.player.setVelocityY(-750); } } createDoors(){ let door = this.physics.add.staticGroup(); // Entry door is inactive door.create(400, 508, 'door', 0, true, false); // Add an exit door door.create(700, 508, 'door', 3); return door; } createDoorSign(){ this.add.text(380, 410, 'LEVEL', { fontSize: '12px', fill: '#ff0000', align: 'center', fontFamily: 'sans-serif' }); return this.add.text(385, 420, '0', { fontSize: '48px', fill: '#ff0000', align: 'center', fontFamily: 'sans-serif' }); } createPlatforms() { let platforms = this.physics.add.staticGroup(); platforms.create(400, 270, 'platform'); platforms.create(400, 572, 'ground'); return platforms; } createPlayer() { let player = this.physics.add.sprite(100, 450, 'zeta'); player.setBounce(0.2); player.setCollideWorldBounds(true); return player; } createWalls() { let walls = this.physics.add.staticGroup(); walls.create(0, 300, 'wall').setActive(true); walls.create(800, 300, 'wall').setActive(true); return walls; } handleCollisionWall(event, collider){ if(collider.body.touching.left){ event.setVelocityX(-100); }else if(collider.body.touching.right){ event.setVelocityX(100); } return true; } handleCollisionEnemy(event, collider) { this.scene.start('EndScene'); } handleOverlapDoor(event, collider) { if(collider.active){ // Load the next scene this.scene.start(this.scene.manager.getAt(this.scene.getIndex()+1)); } } initAnimation() { this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('zeta', { start: 0, end: 3 }), frameRate: 20, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'zeta', frame: 4 } ], frameRate: 20 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('zeta', { start: 5, end: 8 }), frameRate: 20, repeat: -1 }); this.anims.create({ key: 'doorOpen', frames: this.anims.generateFrameNumbers('door', { start: 1, end: 3 }), frameRate: 10 }); } spawnBot(){ this.bots.create(100, 75, 'bot').setVelocityX(100); } }
src/scenes/GameScene.js
github.com/mwilber/zeta-bros > Step12
import 'phaser'; import { Level1 } from './levels/level1'; import { Level2 } from './levels/level2'; import { EndScene } from './scenes/EndScene'; const gameConfig = { type: Phaser.AUTO, width: 800, height: 600, parent: 'phaser-game', dom: { createContainer: true }, physics: { default: 'arcade', arcade: { gravity: { y: 900 }, debug: false } }, scene: [ Level1, Level2, EndScene ] }; new Phaser.Game(gameConfig);
src/main.js
import { GameScene } from '../scenes/GameScene'; export class Level1 extends GameScene{ constructor() { super({ key: 'Level1' }); this.levelCt = 1; } createPlatforms() { let platforms = super.createPlatforms(); platforms.create(100, 180, 'platform'); platforms.create(500, 180, 'platform'); platforms.create(700, 360, 'platform'); platforms.create(300, 360, 'platform'); return platforms; } createDoors(){ let door = super.createDoors(); // Add an exit door door.create(100, 132, 'door', 3); return door; } create() { super.create(); this.time.addEvent({ delay: 5000, callback: (event)=>{ this.spawnBot('left'); }, callbackScope: this, repeat: 1 }); } }
src/levels/Level1.js
github.com/mwilber/zeta-bros > Step13
import 'phaser'; import { IntroScene } from './scenes/IntroScene'; import { Level1 } from './levels/level1'; import { Level2 } from './levels/level2'; import { EndScene } from './scenes/EndScene'; const gameConfig = { type: Phaser.AUTO, width: 800, height: 600, parent: 'phaser-game', dom: { createContainer: true }, physics: { default: 'arcade', arcade: { gravity: { y: 900 }, debug: false } }, scene: [ IntroScene, Level1, Level2, EndScene ] }; new Phaser.Game(gameConfig);
src/main.js
import Phaser from 'phaser'; export class IntroScene extends Phaser.Scene { constructor() { super({ key: 'IntroScene' }); } preload() { this.load.image('splash', 'assets/images/intro_splash.png'); } create() { // Add the background image this.splash = this.add.image(400, 300, 'splash'); this.splash.setInteractive().on('pointerdown', () => { this.scene.start('Level1'); }); } }
src/scenes/IntroScene.js
Source Code
github.com/mwilber/zeta-bros
Follow @greenzeta on Twitter
Slides
slides.com/greenzeta/phaser
greenzeta.com
Phaser Website
phaser.io
By Matthew Wilber
From the 9/10/2019 Fredericksburg Developer Group meeting.
Full-Stack Web Developer. Building web sites & mobile apps for fun and profit! Free-time freelancer and UI/UX Engineer @ChartIQ