/// /// TODO: Custom Asset: FmodEventMapper /// use crate::prelude::*; use bevy::prelude::*; use bevy_fmod::prelude::AudioSource; use bevy_fmod::prelude::*; pub(crate) struct AudioPlugin; impl Plugin for AudioPlugin { fn build(&self, app: &mut App) { app.add_event::(); app.add_plugins(FmodPlugin { audio_banks_paths: &[ "./assets/audio/Martian Chess Audio/Build/Desktop/Master.bank", "./assets/audio/Martian Chess Audio/Build/Desktop/Master.strings.bank", "./assets/audio/Martian Chess Audio/Build/Desktop/Music.bank", "./assets/audio/Martian Chess Audio/Build/Desktop/SFX.bank", ], }); app.init_resource::(); app.add_systems(OnEnter(GameState::Intro), play_background); app.add_systems( Update, ( play_audio.run_if(any_component_added::), audio_trigger.run_if(on_event::()), control_volume.run_if(resource_changed::()), toggle_volume .run_if(|keys: Res>| -> bool { keys.just_pressed(KeyCode::M) }), ), ); } } #[derive(Event, Debug, PartialEq, Component, Clone)] pub enum AudioEvent { MainMusic, _StopMainMusic, MenuHover, MenuSelect, PickUp, PutDown, Idle, StopIdle, Invalid, } #[derive(Resource, Debug)] pub struct AudioVolume(f32); impl Default for AudioVolume { fn default() -> Self { AudioVolume(1.0) } } fn play_background(mut events: EventWriter) { events.send(AudioEvent::MainMusic); } fn play_audio(mut events: Query<&AudioSource, Added>) { events.iter_mut().for_each(|aud| aud.play()); } fn audio_trigger( mut events: EventReader, sources: Query<(Entity, &AudioSource, &AudioEvent)>, studio: Res, mut commands: Commands, server: Res, tweaks: Res>, display_state: Res>, vol: Res, ) { let tweak = tweaks .get(&server.load("martian.tweak.toml")) .expect("Load tweaks"); let state = display_state.get(); events.read().for_each(|event| { let aud = match event { AudioEvent::MainMusic | AudioEvent::_StopMainMusic => { tweak.get::("audio_music_main").unwrap() } AudioEvent::MenuHover => tweak.get::("audio_menu_hover").unwrap(), AudioEvent::MenuSelect => tweak.get::("audio_menu_select").unwrap(), AudioEvent::PickUp => tweak.get::("audio_display3d_pick_up").unwrap(), AudioEvent::PutDown => tweak.get::("audio_display3d_put_down").unwrap(), AudioEvent::Idle | AudioEvent::StopIdle => { tweak.get::("audio_display3d_idle").unwrap() } AudioEvent::Invalid => tweak.get::("audio_display3d_invalid").unwrap(), }; // There is an event, play an audio if !aud.is_empty() { let event_str = format!("event:{}", aud); if let Ok(event_description) = studio.0.get_event(event_str.as_str()) { // We are stopping a playing event match event { // Find and stop playing instances of idle audio AudioEvent::StopIdle => { sources .iter() .filter(|(_, _, source_event)| **source_event == AudioEvent::Idle) .for_each(|(entity, source, _)| { info!("Stopping audio {}", event_str); source.stop(); commands.entity(entity).despawn_recursive(); }); } // Find and stop playing instances of main music AudioEvent::_StopMainMusic => { sources .iter() .filter(|(_, _, source_event)| **source_event == AudioEvent::MainMusic) .for_each(|(entity, source, _)| { info!("Stopping audio {}", event_str); source.stop(); commands.entity(entity).despawn_recursive(); }); } // we are playing a sound _ => { info!("Playing audio {}", event_str); let audio_source = AudioSource::new(event_description); audio_source.set_volume(vol.0); // TODO: Can we batch spawn all sounds at startup? Then Start/Stop/Reset at runtime? commands.spawn((audio_source, event.clone())); } } } else { warn!("No music set for {:?} in {:?}", event, state); } } }); } fn toggle_volume(mut vol: ResMut) { if vol.0 > 0.0 { vol.0 = 0.0; } else { vol.0 = 1.0; } } fn control_volume(vol: Res, query: Query<&AudioSource>) { query .iter() .for_each(|aud_src| aud_src.set_volume((*vol).0)); }