Introduction
Now it’s starting to feel like a game! With the draw countdown, the bandits drawing their weapons, and the “BANG!” animation - this is really coming together!
I’ve moved a few things around, as I’ve made a technical decision to use:
this.scene.start('GameOverScene')
to completely come out of the game (in
MainScene
) once the player has been shot.
This seemed like a good decision, else I’d need to cancel the timers, etc - and
now, I can just rely on the scene change to run housekeeping for me.
So … Now, the asset preloading and animation creation happens in a “Pre-Game”
BootScene
:
class BootScene extends Phaser.Scene {
constructor() {
super('BootScene');
}
preload() {
// Preload the bandit assets.
for (let i = 1; i <= 3; i++) {
// There are four frames for each bandit.
for (let f = 1; f <= 4; f++) {
this.load.image(`bandit-${i}-${f}`, `/images/westbank/bandit-${i}-${f}.png`);
}
}
// Preload the font assets.
for (let i = 0; i <= 9; i++) {
this.load.image(`char-${i}`, `/images/westbank/char-${i}.png`);
}
// Preload the score/ lives assets.
const uiElements = ['score-text', 'lives-text', 'lives'];
uiElements.forEach(element => {
this.load.image(element, `/images/westbank/${element}.png`);
});
// Preload the bang assets.
for (let i = 1; i <= 6; i++) {
this.load.image(`bang-${i}`, `/images/westbank/bang-${i}.png`);
}
}
create() {
// Create the bandit animations.
for (let i = 1; i <= 3; i++) {
const frameNames = Array.from({length: 4}, (_, f) => `bandit-${i}-${f+1}`);
this.anims.create({
key: `bandit-${i}-anim`,
frames: frameNames.map(frameName => ({ key: frameName })),
frameRate: 2,
repeat: 0
});
}
// Create the bang animation.
this.anims.create({
key: 'bang-anim',
frames: Array.from({ length: 6 }, (_, i) => ({ key: `bang-${i + 1}` })),
frameRate: 8,
repeat: 0
});
// Start the game.
this.scene.start('MainScene');
}
}
It’s pretty fast though, so I don’t feel like it needs it for this short game.
The MainScene
now obviously doesn’t need those parts any more, and of course,
all of the methods I had before are still there, e.g. printAt()
,
decreaseCountdown()
, etc.
The only big differences are:
class MainScene extends Phaser.Scene {
...
..
.
create() {
// Clear down the bandits.
this.bandits = [];
// Create and place the bandit sprites.
for (let i = 1; i <= 3; i++) {
const bandit = this.add.sprite(
8 * ((i * 9) - 2) * this.gameScale,
8 * 12 * this.gameScale,
`bandit-${i}-1`
);
this.bandits.push(bandit);
}
// Cycle through each bandit and set him up.
this.bandits.forEach((bandit, index) => {
bandit.frameNames = Array.from({length: 4}, (_, f) => `bandit-${index + 1}-${f+1}`);
// Property for how long before this bandit will draw their weapon.
bandit.drawTimer = this.time.delayedCall(
// 5 seconds + 0 - 3 second delay.
Phaser.Math.Between(0, 30) * 100 + 5000,
() => this.banditDraw(index),
[],
this
);
// Stub property for holding a timer for how long before the bandit fires.
bandit.fireTimer = false;
// Helper property to note that the bandit has drawn their weapon.
bandit.hasDrawn = false;
});
}
banditDraw(index) {
const bandit = this.bandits[index];
// Clear the draw timer reference.
bandit.drawTimer = null;
// Set the bandit draw sprite.
bandit.setTexture(`bandit-${index + 1}-2`);
// Set that this bandit has now drawn.
bandit.hasDrawn = true;
// Initiate the "time-to-fire" countdown.
bandit.fireTimer = this.time.delayedCall(
// Allow 2 seconds for the player to react.
2000,
() => this.gameOver(),
[],
this
);
}
gameOver() {
this.scene.start('GameOverScene');
}
}
- Equally, I could have just started each
bandit.fireTimer
on completion of the game countdown timer. I just considered the game countdown as scene decoration and just adding 5 seconds to thebandit.fireTimer
so we know that it’ll begin after the game countdown has finished just seemed neat enough. - I’ve created
bandit.hasDrawn
here, this will be used in-game for a check to see if you fire too early (which you’re penalised for), just it’s unused here. - After the bandit has drawn their weapon, there’s a small delay before they fire - I’m going to implement a “level selection” I think and have this configurable.
And then finally, I’ve added in the “BANG!” animation which also appears in the game:
class GameOverScene extends Phaser.Scene {
constructor() {
super('GameOverScene');
}
create() {
// Place the bang animation.
const bang = this.add.sprite(
config.width / 2,
config.height / 2,
'bang-1'
);
// And then play it.
bang.play('bang-anim');
// Return to main scene after animation.
bang.on('animationcomplete', () => {
// Introduce a slight delay for cosmetic reasons.
this.time.delayedCall(200, () => {
this.scene.start('MainScene');
});
});
}
}
There’s no keyboard/ mouse interaction - this just demos the whole loop cycling through over-and-over: