/// /// TODO: Custom Asset: FmodEventMapper /// use crate::prelude::*; use bevy_fmod::prelude::AudioSource; use bevy_fmod::prelude::*; use serde::Deserialize; use bevy::{ asset::{AssetLoader, LoadContext, LoadedAsset}, prelude::*, reflect::{TypePath, TypeUuid}, utils::BoxedFuture, }; pub(crate) struct AudioPlugin; impl Plugin for AudioPlugin { fn build(&self, app: &mut App) { app.add_event::(); app.add_systems(OnEnter(GameState::Loading), load_tweakfile); 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.add_asset::().init_asset_loader::(); app.add_systems(OnEnter(GameState::Menu), play_background); app.add_systems( Update, ( play_audio.run_if(any_component_added::), audio_trigger.run_if(on_event::()), // play_background.run_if(on_event::>()), TODO: Handle music hot-reload ), ); } } #[derive(Event, Debug)] pub enum AudioEvent { MainMusic, MenuSelect, PickUp, PutDown, Idle, Invalid, } #[derive(Resource)] struct AudioTweakfile(Handle); fn load_tweakfile( server: Res, mut commands: Commands, ) { let handle: Handle = server.load("audio/martian.audio.tweak"); commands.insert_resource(AudioTweakfile(handle)); } 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, studio: Res, mut commands: Commands, server: Res, audio_settings: Res>, display_state: Res>, ) { let tweak = audio_settings.get(&server.load("audio/martian.audio.tweak")).expect("Load audio tweakfile"); debug!("Audio tweaks: {:?}", tweak); let state = display_state.get(); events.iter().for_each(|event| { let aud = match event { AudioEvent::MainMusic => tweak.music.main.clone(), AudioEvent::MenuSelect => tweak.menu.select.clone(), AudioEvent::PickUp => match state { DisplayState::Display2d => tweak.display2d.pick_up.clone(), DisplayState::Display3d => tweak.display3d.pick_up.clone(), }, AudioEvent::PutDown => match state { DisplayState::Display2d => tweak.display2d.put_down.clone(), DisplayState::Display3d => tweak.display3d.put_down.clone(), }, AudioEvent::Idle => match state { DisplayState::Display2d => tweak.display2d.idle.clone(), DisplayState::Display3d => tweak.display3d.idle.clone(), }, AudioEvent::Invalid => match state { DisplayState::Display2d => tweak.display2d.invalid.clone(), DisplayState::Display3d => tweak.display3d.invalid.clone(), }, }; if !aud.is_empty() { let event_str = format!("event:{}", aud); if let Ok(event_description) = studio.0.get_event(event_str.as_str()) { info!("Playing FMOD {:?}", event_str); commands.spawn(AudioSource::new(event_description)); } else { warn!("Music not found for {:?}", event_str); } } else { warn!("No music set for {:?} in {:?}", event, state); } }); } #[derive(Debug, Deserialize, TypeUuid, TypePath)] #[uuid = "5c7a67ee-56a9-4fe9-9e91-1a709fbc3cf0"] pub(crate) struct AudioSettings { #[serde(default)] display2d: PlaySettings, #[serde(default)] display3d: PlaySettings, #[serde(default)] music: MusicSettings, #[serde(default)] menu: MenuSettings, } #[derive(Debug, Deserialize, Default)] struct MusicSettings { #[serde(default)] main: String, } #[derive(Debug, Deserialize, Default)] struct MenuSettings { #[serde(default)] select: String, } #[derive(Debug, Deserialize, Default)] struct PlaySettings { #[serde(default)] pick_up: String, #[serde(default)] put_down: String, #[serde(default)] idle: String, #[serde(default)] invalid: String, } #[derive(Default)] pub struct AudioSettingsLoader; impl AssetLoader for AudioSettingsLoader { fn load<'a>( &'a self, bytes: &'a [u8], load_context: &'a mut LoadContext, ) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> { Box::pin(async move { let s = std::str::from_utf8(bytes)?; let custom_asset = toml::from_str::(s)?; load_context.set_default_asset(LoadedAsset::new(custom_asset)); Ok(()) }) } fn extensions(&self) -> &[&str] { &["audio.tweak"] } }