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();}}