diff --git a/life/assets/audioOff.png b/life/assets/audioOff.png new file mode 100644 index 0000000..bf3622b Binary files /dev/null and b/life/assets/audioOff.png differ diff --git a/life/assets/audioOn.png b/life/assets/audioOn.png new file mode 100644 index 0000000..65a3194 Binary files /dev/null and b/life/assets/audioOn.png differ diff --git a/life/src/main.rs b/life/src/main.rs index 18e733d..ae9d6f6 100644 --- a/life/src/main.rs +++ b/life/src/main.rs @@ -22,10 +22,10 @@ fn main() { .init_resource::() .init_resource::() .init_resource::() - .init_state::() + .init_state::() + .init_state::() .insert_resource(GlobalVolume::new(Volume::Linear(1.0))) .add_message::() - .add_message::() .add_systems(Startup, setup) .add_systems(Startup, load_audio_tones) .add_systems(Startup, load_materials) @@ -36,7 +36,9 @@ fn main() { ) .add_systems( Update, - modulate_volume, // TODO: .run_if(any_component_added::) + // Run if any component added/removed: AudioSink + // Run if state changed: AudioPlayback + control_volume, ) .add_systems(Update, grid_gizmo) .add_systems( @@ -57,12 +59,12 @@ fn main() { .add_systems( Update, simulation_step - .run_if(in_state(SimulationState::Run)) + .run_if(in_state(SimulationPlayback::Run)) .run_if(cooldown_secs(FRAME_DURATION)), ) .add_systems( Update, - simulation_step.run_if(on_message::), + simulation_step.run_if(state_changed::), ) .add_systems( Update, @@ -71,6 +73,14 @@ fn main() { .add_systems(Update, cell_lifecycle.run_if(on_message::)) .add_systems(Update, mouse_pan.run_if(on_message::)) .add_systems(Update, set_pan_state) + .add_systems( + Update, + toggle_state_entities::.run_if(state_changed::), + ) + .add_systems( + Update, + toggle_state_entities::.run_if(state_changed::), + ) .add_observer(on_add_ui_button) .add_observer(on_add_coordinates) .run(); @@ -78,6 +88,13 @@ fn main() { const SCALE: f32 = 100.0; +#[derive(States, Debug, Default, Hash, PartialEq, Eq, Clone, Component)] +enum AudioPlayback { + #[default] + Mute, + Play, +} + #[derive(Debug, Component, PartialEq)] #[require(Visibility, Transform)] enum UiButton { @@ -87,6 +104,8 @@ enum UiButton { Play, ZoomOut, ZoomIn, + PlayAudio, + MuteAudio, } impl UiButton { @@ -98,6 +117,8 @@ impl UiButton { Self::Play => "forward.png", Self::ZoomOut => "zoomOut.png", Self::ZoomIn => "zoomIn.png", + Self::PlayAudio => "audioOn.png", + Self::MuteAudio => "audioOff.png", } } } @@ -171,32 +192,38 @@ fn setup(mut commands: Commands) { )) .with_children(|parent| { parent.spawn(UiButton::Power).observe(shutdown); - // TODO: Play/Pause Toggle - parent.spawn(UiButton::Pause).observe(pause_sim); - parent.spawn(UiButton::Play).observe(play_sim); + // TODO: Reset button + parent + .spawn((UiButton::Pause, SimulationPlayback::Run)) + .observe(pause_sim); + parent + .spawn((UiButton::Play, SimulationPlayback::Pause)) + .observe(play_sim); parent.spawn(UiButton::Step).observe(step_sim); parent.spawn(UiButton::ZoomOut).observe(zoom_out); parent.spawn(UiButton::ZoomIn).observe(zoom_in); - // TODO: Reset button // TODO: Mute/Unmute Toggle + parent + .spawn((UiButton::MuteAudio, AudioPlayback::Play)) + .observe(mute_audio); + parent + .spawn((UiButton::PlayAudio, AudioPlayback::Mute)) + .observe(play_audio); }); fn shutdown(_trigger: On>, mut writer: MessageWriter) { writer.write(AppExit::Success); } - fn pause_sim(_trigger: On>, mut next: ResMut>) { - next.set(SimulationState::Pause); + fn play_sim(_trigger: On>, mut next: ResMut>) { + // Set simulation state + next.set(SimulationPlayback::Run); } - fn step_sim( - _trigger: On>, - mut writer: MessageWriter, - mut next: ResMut>, - ) { - next.set(SimulationState::Step); - writer.write(SimulationState::Step); + fn pause_sim(_trigger: On>, mut next: ResMut>) { + // Set simulation state + next.set(SimulationPlayback::Pause); } - fn play_sim(_trigger: On>, mut next: ResMut>) { - next.set(SimulationState::Run); + fn step_sim(_trigger: On>, mut next: ResMut>) { + next.set(SimulationPlayback::Step); } fn zoom_out(_trigger: On>, mut projection: Query<&mut Projection>) { projection.iter_mut().for_each(|mut p| match *p { @@ -214,6 +241,12 @@ fn setup(mut commands: Commands) { _ => todo!(), }); } + fn play_audio(_trigger: On>, mut next: ResMut>) { + next.set(AudioPlayback::Play); + } + fn mute_audio(_trigger: On>, mut next: ResMut>) { + next.set(AudioPlayback::Mute); + } } #[derive(Debug, Default, Clone, PartialEq, Hash, Eq, Component, Resource)] @@ -398,8 +431,8 @@ fn assert_cell_uniqueness(q: Query<(Entity, &Coordinates), With>) { }) } -#[derive(States, Debug, Default, Hash, PartialEq, Eq, Clone, Message)] -enum SimulationState { +#[derive(States, Debug, Default, Hash, PartialEq, Eq, Clone, Component)] +enum SimulationPlayback { #[default] Pause, Run, @@ -532,13 +565,13 @@ fn cooldown_secs(input: f32) -> impl FnMut(Local, Res