Sound effects
In this section we will work on adding sound effects. In short, we want to play sounds in these circumstances:
- when the player hits a wall or an obstacle - to let them know they cannot get through
- when the player places a box on the correct spot - as an indication of "you've done it correctly"
- when the player places a box on the incorrect spot - as an indication that the move was wrong
Audio store
Now in order to play the sound the wav files need to be loaded. To avoid loading them on the fly every time before we play the sound we'll create an audio store and load them up at the beginning of the game.
We'll use a resource for the audio store.
#![allow(unused)] fn main() { #[derive(Default)] pub struct AudioStore { pub sounds: HashMap<String, std::boxed::Box<audio::Source>>, } }
And let's add the code for initializing the store, which means pre-loading all the sounds needed for the game.
#![allow(unused)] fn main() { pub fn load_sounds(world: &mut World, context: &mut Context) { let mut query = world.query::<&mut crate::components::AudioStore>(); let audio_store = query.iter().next().unwrap().1; let sounds = ["correct", "incorrect", "wall"]; for sound in sounds.iter() { let sound_name = sound.to_string(); let sound_path = format!("/sounds/{}.wav", sound_name); let sound_source = Source::new(context, sound_path).expect("expected sound loaded"); audio_store .sounds .insert(sound_name, Box::new(sound_source)); } } }
And then call this function when we are initializing the level.
#![allow(unused)] fn main() { pub fn initialize_level(world: &mut World, context: &mut Context) { const MAP: &str = " N N W W W W W W W W W . . . . W W . . . BB . . W W . . RB . . . W W . P . . . . W W . . . . RS . W W . . BS . . . W W . . . . . . W W W W W W W W W "; load_map(world, MAP.to_string()); load_sounds(world, context); } }
Playing audio
Finally, let's add the ability to play the sound in the store.
#![allow(unused)] fn main() { impl AudioStore { pub fn play_sound(&mut self, context: &mut Context, sound: &str) { if let Some(source) = self.sounds.get_mut(sound) { if source.play_detached(context).is_ok() { println!("Playing sound: {}", sound); } } } } }
And now let's play in the event system.
#![allow(unused)] fn main() { // systems/events.rs use crate::components::*; use crate::events::*; use ggez::Context; use hecs::World; use std::collections::HashMap; pub fn run_process_events(world: &mut World, context: &mut Context) { let events = { let mut query = world.query::<&mut crate::components::EventQueue>(); let events = query .iter() .next() .unwrap() .1 .events .drain(..) .collect::<Vec<_>>(); events }; let mut new_events = Vec::new(); let mut query = world.query::<(&Position, &BoxSpot)>(); let box_spots_by_position: HashMap<(u8, u8), &BoxSpot> = query .iter() .map(|(_, t)| ((t.0.x, t.0.y), t.1)) .collect::<HashMap<_, _>>(); let mut query = world.query::<&mut AudioStore>(); let audio_store = query.iter().next().unwrap().1; for event in events { println!("New event: {:?}", event); match event { Event::PlayerHitObstacle => { // play sound here audio_store.play_sound(context, "wall"); } Event::EntityMoved(EntityMoved { entity }) => { // An entity was just moved, check if it was a box and fire // more events if it's been moved on a spot. if let Ok(the_box) = world.get::<&Box>(entity) { if let Ok(box_position) = world.get::<&Position>(entity) { // Check if there is a spot on this position, and if there // is if it's the correct or incorrect type if let Some(box_spot) = box_spots_by_position.get(&(box_position.x, box_position.y)) { new_events.push(Event::BoxPlacedOnSpot(BoxPlacedOnSpot { is_correct_spot: (box_spot.colour == the_box.colour), })); } } } } Event::BoxPlacedOnSpot(BoxPlacedOnSpot { is_correct_spot }) => { // play sound here let sound = if is_correct_spot { "correct" } else { "incorrect" }; audio_store.play_sound(context, sound); } } } // Finally add events back into the world { let mut query = world.query::<&mut EventQueue>(); let event_queue = query.iter().next().unwrap().1; event_queue.events.append(&mut new_events); } } }
Now let's run the game and enjoy those sound effects!
CODELINK: You can see the full code in this example here.