Build games as easily as you play them — join the Phaser Beam waitlist for Early Access.
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);
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);