Step 15 - Flappy Sounds and Music
Finally to really add depth to a game let's add some sound! ex.Sound
needs to be loaded much like ex.ImageSource
.
Excalibur supports any audio your browser supports, you can specify an ordered list of files to fallback to if a browser doesn't support.
typescript
// resources.tsexport const Resources = {// Relative to /public in vite...// SoundsFlapSound: new ex.Sound('./sounds/flap.wav'),FailSound: new ex.Sound('./sounds/fail.wav'),ScoreSound: new ex.Sound('./sounds/score.wav'),// MusicBackgroundMusic: new ex.Sound('./sounds/two_left_socks.ogg')} as const;
typescript
// resources.tsexport const Resources = {// Relative to /public in vite...// SoundsFlapSound: new ex.Sound('./sounds/flap.wav'),FailSound: new ex.Sound('./sounds/fail.wav'),ScoreSound: new ex.Sound('./sounds/score.wav'),// MusicBackgroundMusic: new ex.Sound('./sounds/two_left_socks.ogg')} as const;
You can leverage the scene lifecycle in level.ts
with onActivate()
to start some looping background music.
typescript
// level.tsexport class Level extends ex.Scene {...override onActivate(): void {Resources.BackgroundMusic.loop = true;Resources.BackgroundMusic.play();}...}
typescript
// level.tsexport class Level extends ex.Scene {...override onActivate(): void {Resources.BackgroundMusic.loop = true;Resources.BackgroundMusic.play();}...}
We also want a "flap" sound effect every time the bird flaps it's wings.
typescript
// bird.tsexport class Bird extends ex.Actor {...override onPostUpdate(engine: ex.Engine): void {if (!this.playing) return;// if the space bar or the first pointer was downif (!this.jumping && this.isInputActive(engine)) {...// play sound effectResources.FlapSound.play();}}}
typescript
// bird.tsexport class Bird extends ex.Actor {...override onPostUpdate(engine: ex.Engine): void {if (!this.playing) return;// if the space bar or the first pointer was downif (!this.jumping && this.isInputActive(engine)) {...// play sound effectResources.FlapSound.play();}}}
The user needs some rewarding sound when they score points, let's add that to our score trigger.
typescript
// score-trigger.tsexport class ScoreTrigger extends ex.Actor {...override onCollisionStart(): void {...Resources.ScoreSound.play();}}
typescript
// score-trigger.tsexport class ScoreTrigger extends ex.Actor {...override onCollisionStart(): void {...Resources.ScoreSound.play();}}
Finally we need a game over sound effect!
typescript
// level.tsexport class Level extends ex.Scene {...triggerGameOver() {...Resources.FailSound.play();}}
typescript
// level.tsexport class Level extends ex.Scene {...triggerGameOver() {...Resources.FailSound.play();}}