export default class Germ extends Phaser.Physics.Arcade.Sprite { constructor (scene, x, y, animation, speed) { super(scene, x, y, 'https://labs.phaser.io/assets'); this.play(animation) this.setScale(Phaser.Math.FloatBetween(1, 2)); this.speed = speed; this.alpha = 0; this.lifespan = 0; this.isChasing = false; this.target = new Phaser.Math.Vector2(); } start (chaseDelay) { this.setCircle(14, 6, 2); if (!chaseDelay) { chaseDelay = Phaser.Math.RND.between(3000, 8000); this.scene.sound.play('appear'); } this.scene.tweens.add({ targets: this, alpha: 1, duration: 2000, ease: 'Linear', hold: chaseDelay, onComplete: () => { if (this.scene.player.isAlive) { this.lifespan = Phaser.Math.RND.between(6000, 12000); this.isChasing = true; } } }); return this; } restart (x, y) { this.body.reset(x, y); this.setActive(true); this.setVisible(true); this.setAlpha(0); return this.start(); } preUpdate (time, delta) { super.preUpdate(time, delta); if (this.isChasing) { this.lifespan -= delta; if (this.lifespan <= 0) { this.isChasing = false; this.body.stop(); this.scene.tweens.add({ targets: this, alpha: 0, duration: 1000, ease: 'Linear', onComplete: () => { this.setActive(false); this.setVisible(false); } }); } else { this.scene.getPlayer(this.target); // Add 90 degrees because the sprite is drawn facing up this.rotation = this.scene.physics.moveToObject(this, this.target, this.speed) + 1.5707963267948966; } } } stop () { this.isChasing = false; this.body.stop(); } }
export default class Pickups extends Phaser.Physics.Arcade.Group { constructor (world, scene) { super(world, scene); this.outer = new Phaser.Geom.Rectangle(64, 64, 672, 472); this.target = new Phaser.Geom.Point(); } start () { this.create(400, 100, 'https://labs.phaser.io/assets', 'ring'); this.create(100, 380, 'https://labs.phaser.io/assets', 'ring'); this.create(700, 380, 'https://labs.phaser.io/assets', 'ring'); this.create(300, 500, 'https://labs.phaser.io/assets', 'ring'); this.create(500, 500, 'https://labs.phaser.io/assets', 'ring'); } collect (pickup) { // Move the pick-up to a new location this.outer.getRandomPoint(this.target); pickup.body.reset(this.target.x, this.target.y); } }
export default class Boot extends Phaser.Scene { constructor () { super('Boot'); } preload () { this.load.setBaseURL('https://cdn.phaserfiles.com/v355'); this.load.setPath('assets/games/germs/'); this.load.image('background', 'background.png'); this.load.bitmapFont('slime', 'slime-font.png', 'slime-font.xml'); } create () { this.registry.set('highscore', 0); this.scene.start('Preloader'); } }
export default class Player extends Phaser.Physics.Arcade.Image { constructor (scene, x, y) { super(scene, x, y, 'https://labs.phaser.io/assets', 'player'); scene.add.existing(this); scene.physics.add.existing(this); this.setCircle(14, 3, 6); this.setCollideWorldBounds(true); this.isAlive = false; this.speed = 280; this.target = new Phaser.Math.Vector2(); } start () { this.isAlive = true; this.scene.input.on('pointermove', (pointer) => { if (this.isAlive) { this.target.x = pointer.x; this.target.y = pointer.y; // Add 90 degrees because the sprite is drawn facing up this.rotation = this.scene.physics.moveToObject(this, this.target, this.speed) + 1.5707963267948966; } }); } kill () { this.isAlive = false; this.body.stop(); } preUpdate () { if (this.body.speed > 0 && this.isAlive) { if (Phaser.Math.Distance.Between(this.x, this.y, this.target.x, this.target.y) < 6) { this.body.reset(this.target.x, this.target.y); } } } }
export default class MainMenu extends Phaser.Scene { constructor () { super('MainMenu'); } create () { this.music = this.sound.play('music', { loop: true }); this.sound.play('laugh'); this.add.image(400, 300, 'background').setScale(2); const area = new Phaser.Geom.Rectangle(64, 64, 672, 472); this.addGerm(area, 'germ1'); this.addGerm(area, 'germ2'); this.addGerm(area, 'germ3'); this.addGerm(area, 'germ4'); this.add.shader('goo', 400, 300, 800, 600); this.add.image(400, 260, 'https://labs.phaser.io/assets', 'logo'); this.add.bitmapText(400, 500, 'slime', 'Click to Play', 40).setOrigin(0.5); this.input.once('pointerdown', () => { this.scene.start('MainGame'); }); } addGerm (area, animation) { let start = area.getRandomPoint(); let germ = this.add.sprite(start.x, start.y).play(animation).setScale(2); let durationX = Phaser.Math.Between(4000, 6000); let durationY = durationX + 3000; this.tweens.add({ targets: germ, x: { getStart: (tween, target) => { return germ.x; }, getEnd: () => { return area.getRandomPoint().x; }, duration: durationX, ease: 'Linear' }, y: { getStart: (tween, target) => { return germ.y; }, getEnd: () => { return area.getRandomPoint().y; }, duration: durationY, ease: 'Linear' }, repeat: -1 }); } }
export default class Preloader extends Phaser.Scene { constructor () { super('Preloader'); } preload () { this.load.setBaseURL('https://cdn.phaserfiles.com/v355'); this.add.image(400, 300, 'background').setScale(2); this.loadText = this.add.bitmapText(400, 300, 'slime', 'Loading ...', 80).setOrigin(0.5); this.load.setPath('assets/games/germs/'); this.load.atlas('https://labs.phaser.io/assets', 'germs.png', 'germs.json'); this.load.glsl('goo', 'goo.glsl.js'); // Audio ... this.load.setPath('assets/games/germs/sounds/'); this.load.audio('appear', [ 'appear.ogg', 'appear.m4a', 'appear.mp3' ]); this.load.audio('fail', [ 'fail.ogg', 'fail.m4a', 'fail.mp3' ]); this.load.audio('laugh', [ 'laugh.ogg', 'laugh.m4a', 'laugh.mp3' ]); this.load.audio('music', [ 'music.ogg', 'music.m4a', 'music.mp3' ]); this.load.audio('pickup', [ 'pickup.ogg', 'pickup.m4a', 'pickup.mp3' ]); this.load.audio('start', [ 'start.ogg', 'start.m4a', 'start.mp3' ]); this.load.audio('victory', [ 'victory.ogg', 'victory.m4a', 'victory.mp3' ]); } create () { // Create our global animations this.anims.create({ key: 'germ1', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'red', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'germ2', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'green', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'germ3', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'blue', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'germ4', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'purple', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); if (this.sound.locked) { this.loadText.setText('Click to Start'); this.input.once('pointerdown', () => { this.scene.start('MainMenu'); }); } else { this.scene.start('MainMenu'); } } }
import Germs from './Germs.js'; import Player from './Player.js'; import Pickups from './Pickups.js'; export default class MainGame extends Phaser.Scene { constructor () { super('MainGame'); this.player; this.germs; this.pickups; this.introText; this.scoreText; this.score = 0; this.highscore = 0; this.newHighscore = false; } create () { this.score = 0; this.highscore = this.registry.get('highscore'); this.newHighscore = false; this.add.image(400, 300, 'background').setScale(2); this.germs = new Germs(this.physics.world, this); this.pickups = new Pickups(this.physics.world, this); this.player = new Player(this, 400, 400); this.scoreText = this.add.bitmapText(16, 32, 'slime', 'Score 0', 40).setDepth(1); this.introText = this.add.bitmapText(400, 300, 'slime', 'Avoid the Germs\nCollect the Rings', 60).setOrigin(0.5).setCenterAlign().setDepth(1); this.pickups.start(); this.input.once('pointerdown', () => { this.player.start(); this.germs.start(); this.sound.play('start'); this.tweens.add({ targets: this.introText, alpha: 0, duration: 300 }); }); this.physics.add.overlap(this.player, this.pickups, (player, pickup) => this.playerHitPickup(player, pickup)); this.physics.add.overlap(this.player, this.germs, (player, germ) => this.playerHitGerm(player, germ)); } playerHitGerm (player, germ) { // We don't count a hit if the germ is fading in or out if (player.isAlive && germ.alpha === 1) { this.gameOver(); } } playerHitPickup (player, pickup) { this.score++; this.scoreText.setText('Score ' + this.score); if (!this.newHighscore && this.score > this.highscore) { if (this.highscore > 0) { // Only play the victory sound if they actually set a new highscore this.sound.play('victory'); } else { this.sound.play('pickup'); } this.newHighscore = true; } else { this.sound.play('pickup'); } this.pickups.collect(pickup); } gameOver () { this.player.kill(); this.germs.stop(); this.sound.stopAll(); this.sound.play('fail'); this.introText.setText('Game Over!'); this.tweens.add({ targets: this.introText, alpha: 1, duration: 300 }); if (this.newHighscore) { this.registry.set('highscore', this.score); } this.input.once('pointerdown', () => { this.scene.start('MainMenu'); }); } getPlayer (target) { target.x = this.player.x; target.y = this.player.y; return target; } }
import Germ from './Germ.js'; export default class Germs extends Phaser.Physics.Arcade.Group { constructor (world, scene) { super(world, scene); this.classType = Germ; this.germConfig = [ { animation: 'germ1', speed: 60 }, { animation: 'germ2', speed: 90 }, { animation: 'germ3', speed: 120 }, { animation: 'germ4', speed: 180 } ]; } start () { let germ1 = new Germ(this.scene, 100, 100, 'germ1'); let germ2 = new Germ(this.scene, 700, 600, 'germ1'); let germ3 = new Germ(this.scene, 200, 400, 'germ1'); this.add(germ1, true); this.add(germ2, true); this.add(germ3, true); germ1.start(1000); germ2.start(2000); germ3.start(); this.timedEvent = this.scene.time.addEvent({ delay: 2000, callback: this.releaseGerm, callbackScope: this, loop: true }); } stop () { this.timedEvent.remove(); this.getChildren().forEach((child) => { child.stop(); }); } releaseGerm () { const x = Phaser.Math.RND.between(0, 800); const y = Phaser.Math.RND.between(0, 600); let germ; let config = Phaser.Math.RND.pick(this.germConfig); this.getChildren().forEach((child) => { if (child.anims.getName() === config.animation && !child.active) { // We found a dead matching germ, so resurrect it germ = child; } }); if (germ) { germ.restart(x, y); } else { germ = new Germ(this.scene, x, y, config.animation, config.speed); this.add(germ, true); germ.start(); } } }
import Boot from './Boot.js'; import Preloader from './Preloader.js'; import MainMenu from './MainMenu.js'; import MainGame from './Game.js'; const config = { type: Phaser.AUTO, width: 800, height: 600, backgroundColor: '#000000', parent: 'phaser-example', scene: [ Boot, Preloader, MainMenu, MainGame ], physics: { default: 'arcade', arcade: { debug: false } } }; let game = new Phaser.Game(config);
Scan to open on your mobile device
export default class Germ extends Phaser.Physics.Arcade.Sprite { constructor (scene, x, y, animation, speed) { super(scene, x, y, 'https://labs.phaser.io/assets'); this.play(animation) this.setScale(Phaser.Math.FloatBetween(1, 2)); this.speed = speed; this.alpha = 0; this.lifespan = 0; this.isChasing = false; this.target = new Phaser.Math.Vector2(); } start (chaseDelay) { this.setCircle(14, 6, 2); if (!chaseDelay) { chaseDelay = Phaser.Math.RND.between(3000, 8000); this.scene.sound.play('appear'); } this.scene.tweens.add({ targets: this, alpha: 1, duration: 2000, ease: 'Linear', hold: chaseDelay, onComplete: () => { if (this.scene.player.isAlive) { this.lifespan = Phaser.Math.RND.between(6000, 12000); this.isChasing = true; } } }); return this; } restart (x, y) { this.body.reset(x, y); this.setActive(true); this.setVisible(true); this.setAlpha(0); return this.start(); } preUpdate (time, delta) { super.preUpdate(time, delta); if (this.isChasing) { this.lifespan -= delta; if (this.lifespan <= 0) { this.isChasing = false; this.body.stop(); this.scene.tweens.add({ targets: this, alpha: 0, duration: 1000, ease: 'Linear', onComplete: () => { this.setActive(false); this.setVisible(false); } }); } else { this.scene.getPlayer(this.target); // Add 90 degrees because the sprite is drawn facing up this.rotation = this.scene.physics.moveToObject(this, this.target, this.speed) + 1.5707963267948966; } } } stop () { this.isChasing = false; this.body.stop(); } }
export default class Pickups extends Phaser.Physics.Arcade.Group { constructor (world, scene) { super(world, scene); this.outer = new Phaser.Geom.Rectangle(64, 64, 672, 472); this.target = new Phaser.Geom.Point(); } start () { this.create(400, 100, 'https://labs.phaser.io/assets', 'ring'); this.create(100, 380, 'https://labs.phaser.io/assets', 'ring'); this.create(700, 380, 'https://labs.phaser.io/assets', 'ring'); this.create(300, 500, 'https://labs.phaser.io/assets', 'ring'); this.create(500, 500, 'https://labs.phaser.io/assets', 'ring'); } collect (pickup) { // Move the pick-up to a new location this.outer.getRandomPoint(this.target); pickup.body.reset(this.target.x, this.target.y); } }
export default class Boot extends Phaser.Scene { constructor () { super('Boot'); } preload () { this.load.setBaseURL('https://cdn.phaserfiles.com/v355'); this.load.setPath('assets/games/germs/'); this.load.image('background', 'background.png'); this.load.bitmapFont('slime', 'slime-font.png', 'slime-font.xml'); } create () { this.registry.set('highscore', 0); this.scene.start('Preloader'); } }
export default class Player extends Phaser.Physics.Arcade.Image { constructor (scene, x, y) { super(scene, x, y, 'https://labs.phaser.io/assets', 'player'); scene.add.existing(this); scene.physics.add.existing(this); this.setCircle(14, 3, 6); this.setCollideWorldBounds(true); this.isAlive = false; this.speed = 280; this.target = new Phaser.Math.Vector2(); } start () { this.isAlive = true; this.scene.input.on('pointermove', (pointer) => { if (this.isAlive) { this.target.x = pointer.x; this.target.y = pointer.y; // Add 90 degrees because the sprite is drawn facing up this.rotation = this.scene.physics.moveToObject(this, this.target, this.speed) + 1.5707963267948966; } }); } kill () { this.isAlive = false; this.body.stop(); } preUpdate () { if (this.body.speed > 0 && this.isAlive) { if (Phaser.Math.Distance.Between(this.x, this.y, this.target.x, this.target.y) < 6) { this.body.reset(this.target.x, this.target.y); } } } }
export default class MainMenu extends Phaser.Scene { constructor () { super('MainMenu'); } create () { this.music = this.sound.play('music', { loop: true }); this.sound.play('laugh'); this.add.image(400, 300, 'background').setScale(2); const area = new Phaser.Geom.Rectangle(64, 64, 672, 472); this.addGerm(area, 'germ1'); this.addGerm(area, 'germ2'); this.addGerm(area, 'germ3'); this.addGerm(area, 'germ4'); this.add.shader('goo', 400, 300, 800, 600); this.add.image(400, 260, 'https://labs.phaser.io/assets', 'logo'); this.add.bitmapText(400, 500, 'slime', 'Click to Play', 40).setOrigin(0.5); this.input.once('pointerdown', () => { this.scene.start('MainGame'); }); } addGerm (area, animation) { let start = area.getRandomPoint(); let germ = this.add.sprite(start.x, start.y).play(animation).setScale(2); let durationX = Phaser.Math.Between(4000, 6000); let durationY = durationX + 3000; this.tweens.add({ targets: germ, x: { getStart: (tween, target) => { return germ.x; }, getEnd: () => { return area.getRandomPoint().x; }, duration: durationX, ease: 'Linear' }, y: { getStart: (tween, target) => { return germ.y; }, getEnd: () => { return area.getRandomPoint().y; }, duration: durationY, ease: 'Linear' }, repeat: -1 }); } }
export default class Preloader extends Phaser.Scene { constructor () { super('Preloader'); } preload () { this.load.setBaseURL('https://cdn.phaserfiles.com/v355'); this.add.image(400, 300, 'background').setScale(2); this.loadText = this.add.bitmapText(400, 300, 'slime', 'Loading ...', 80).setOrigin(0.5); this.load.setPath('assets/games/germs/'); this.load.atlas('https://labs.phaser.io/assets', 'germs.png', 'germs.json'); this.load.glsl('goo', 'goo.glsl.js'); // Audio ... this.load.setPath('assets/games/germs/sounds/'); this.load.audio('appear', [ 'appear.ogg', 'appear.m4a', 'appear.mp3' ]); this.load.audio('fail', [ 'fail.ogg', 'fail.m4a', 'fail.mp3' ]); this.load.audio('laugh', [ 'laugh.ogg', 'laugh.m4a', 'laugh.mp3' ]); this.load.audio('music', [ 'music.ogg', 'music.m4a', 'music.mp3' ]); this.load.audio('pickup', [ 'pickup.ogg', 'pickup.m4a', 'pickup.mp3' ]); this.load.audio('start', [ 'start.ogg', 'start.m4a', 'start.mp3' ]); this.load.audio('victory', [ 'victory.ogg', 'victory.m4a', 'victory.mp3' ]); } create () { // Create our global animations this.anims.create({ key: 'germ1', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'red', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'germ2', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'green', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'germ3', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'blue', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); this.anims.create({ key: 'germ4', frames: this.anims.generateFrameNames('https://labs.phaser.io/assets', { prefix: 'purple', start: 1, end: 3 }), frameRate: 8, repeat: -1 }); if (this.sound.locked) { this.loadText.setText('Click to Start'); this.input.once('pointerdown', () => { this.scene.start('MainMenu'); }); } else { this.scene.start('MainMenu'); } } }
import Germs from './Germs.js'; import Player from './Player.js'; import Pickups from './Pickups.js'; export default class MainGame extends Phaser.Scene { constructor () { super('MainGame'); this.player; this.germs; this.pickups; this.introText; this.scoreText; this.score = 0; this.highscore = 0; this.newHighscore = false; } create () { this.score = 0; this.highscore = this.registry.get('highscore'); this.newHighscore = false; this.add.image(400, 300, 'background').setScale(2); this.germs = new Germs(this.physics.world, this); this.pickups = new Pickups(this.physics.world, this); this.player = new Player(this, 400, 400); this.scoreText = this.add.bitmapText(16, 32, 'slime', 'Score 0', 40).setDepth(1); this.introText = this.add.bitmapText(400, 300, 'slime', 'Avoid the Germs\nCollect the Rings', 60).setOrigin(0.5).setCenterAlign().setDepth(1); this.pickups.start(); this.input.once('pointerdown', () => { this.player.start(); this.germs.start(); this.sound.play('start'); this.tweens.add({ targets: this.introText, alpha: 0, duration: 300 }); }); this.physics.add.overlap(this.player, this.pickups, (player, pickup) => this.playerHitPickup(player, pickup)); this.physics.add.overlap(this.player, this.germs, (player, germ) => this.playerHitGerm(player, germ)); } playerHitGerm (player, germ) { // We don't count a hit if the germ is fading in or out if (player.isAlive && germ.alpha === 1) { this.gameOver(); } } playerHitPickup (player, pickup) { this.score++; this.scoreText.setText('Score ' + this.score); if (!this.newHighscore && this.score > this.highscore) { if (this.highscore > 0) { // Only play the victory sound if they actually set a new highscore this.sound.play('victory'); } else { this.sound.play('pickup'); } this.newHighscore = true; } else { this.sound.play('pickup'); } this.pickups.collect(pickup); } gameOver () { this.player.kill(); this.germs.stop(); this.sound.stopAll(); this.sound.play('fail'); this.introText.setText('Game Over!'); this.tweens.add({ targets: this.introText, alpha: 1, duration: 300 }); if (this.newHighscore) { this.registry.set('highscore', this.score); } this.input.once('pointerdown', () => { this.scene.start('MainMenu'); }); } getPlayer (target) { target.x = this.player.x; target.y = this.player.y; return target; } }
import Germ from './Germ.js'; export default class Germs extends Phaser.Physics.Arcade.Group { constructor (world, scene) { super(world, scene); this.classType = Germ; this.germConfig = [ { animation: 'germ1', speed: 60 }, { animation: 'germ2', speed: 90 }, { animation: 'germ3', speed: 120 }, { animation: 'germ4', speed: 180 } ]; } start () { let germ1 = new Germ(this.scene, 100, 100, 'germ1'); let germ2 = new Germ(this.scene, 700, 600, 'germ1'); let germ3 = new Germ(this.scene, 200, 400, 'germ1'); this.add(germ1, true); this.add(germ2, true); this.add(germ3, true); germ1.start(1000); germ2.start(2000); germ3.start(); this.timedEvent = this.scene.time.addEvent({ delay: 2000, callback: this.releaseGerm, callbackScope: this, loop: true }); } stop () { this.timedEvent.remove(); this.getChildren().forEach((child) => { child.stop(); }); } releaseGerm () { const x = Phaser.Math.RND.between(0, 800); const y = Phaser.Math.RND.between(0, 600); let germ; let config = Phaser.Math.RND.pick(this.germConfig); this.getChildren().forEach((child) => { if (child.anims.getName() === config.animation && !child.active) { // We found a dead matching germ, so resurrect it germ = child; } }); if (germ) { germ.restart(x, y); } else { germ = new Germ(this.scene, x, y, config.animation, config.speed); this.add(germ, true); germ.start(); } } }
import Boot from './Boot.js'; import Preloader from './Preloader.js'; import MainMenu from './MainMenu.js'; import MainGame from './Game.js'; const config = { type: Phaser.AUTO, width: 800, height: 600, backgroundColor: '#000000', parent: 'phaser-example', scene: [ Boot, Preloader, MainMenu, MainGame ], physics: { default: 'arcade', arcade: { debug: false } } }; let game = new Phaser.Game(config);