Pause menu functionality (#89)
* make menu use actual soundsettings values * allow changing volume in pause menu * persist saving settings
This commit is contained in:
@@ -2,8 +2,9 @@ mod music;
|
||||
mod sounds;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Resource, Reflect, Clone, Copy, Debug)]
|
||||
#[derive(Resource, Reflect, Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
#[reflect(Resource)]
|
||||
pub struct SoundSettings {
|
||||
pub sound_volume: f32,
|
||||
|
||||
@@ -33,6 +33,7 @@ pub mod debug;
|
||||
pub mod enemy;
|
||||
pub mod heal_effect;
|
||||
pub mod player;
|
||||
mod settings;
|
||||
pub mod setup;
|
||||
pub mod steam;
|
||||
pub mod ui;
|
||||
@@ -49,6 +50,7 @@ pub fn plugin(app: &mut App) {
|
||||
audio::plugin,
|
||||
steam::plugin,
|
||||
ui::plugin,
|
||||
settings::plugin,
|
||||
));
|
||||
|
||||
app.add_systems(
|
||||
|
||||
42
crates/hedz_reloaded/src/client/settings.rs
Normal file
42
crates/hedz_reloaded/src/client/settings.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_pkv::prelude::*;
|
||||
|
||||
use crate::client::audio::SoundSettings;
|
||||
|
||||
#[derive(Resource)]
|
||||
struct SaveTimer(Timer);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.insert_resource(PkvStore::new("Rustunit", "HEDZ"));
|
||||
app.insert_resource(SaveTimer(Timer::from_seconds(1.0, TimerMode::Once)));
|
||||
|
||||
app.add_systems(Update, persist_settings);
|
||||
app.add_systems(Startup, load_settings);
|
||||
}
|
||||
|
||||
fn persist_settings(
|
||||
settings: Res<SoundSettings>,
|
||||
mut pkv: ResMut<PkvStore>,
|
||||
mut timer: ResMut<SaveTimer>,
|
||||
time: Res<Time>,
|
||||
) -> Result {
|
||||
if settings.is_changed() {
|
||||
timer.0.reset();
|
||||
}
|
||||
|
||||
timer.0.tick(time.delta());
|
||||
|
||||
if timer.0.just_finished() {
|
||||
pkv.set("audio", &*settings)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_settings(mut settings: ResMut<SoundSettings>, pkv: Res<PkvStore>) -> Result {
|
||||
if let Ok(loaded) = pkv.get::<SoundSettings>("audio") {
|
||||
*settings = loaded;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
GameState, HEDZ_GREEN, HEDZ_PURPLE, client::control::CharacterInputEnabled,
|
||||
GameState, HEDZ_GREEN, HEDZ_PURPLE,
|
||||
client::{audio::SoundSettings, control::CharacterInputEnabled},
|
||||
loading_assets::UIAssets,
|
||||
};
|
||||
use bevy::{color::palettes::css::BLACK, prelude::*};
|
||||
@@ -18,16 +19,29 @@ enum ProgressBar {
|
||||
Sound,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct VolumeValue(ProgressBar);
|
||||
|
||||
#[derive(Resource)]
|
||||
struct PauseMenuSelection(ProgressBar);
|
||||
|
||||
#[derive(Resource)]
|
||||
struct VolumeChangeCooldown(Timer);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.init_state::<PauseMenuState>();
|
||||
|
||||
app.add_systems(Update, open_pause_menu.run_if(in_state(GameState::Playing)));
|
||||
app.add_systems(
|
||||
Update,
|
||||
(selection_input, selection_changed).run_if(in_state(PauseMenuState::Open)),
|
||||
(
|
||||
selection_input,
|
||||
selection_changed,
|
||||
update_volume_display,
|
||||
change_volume_keyboard,
|
||||
change_volume_gamepad,
|
||||
)
|
||||
.run_if(in_state(PauseMenuState::Open)),
|
||||
);
|
||||
app.add_systems(OnEnter(PauseMenuState::Open), setup);
|
||||
}
|
||||
@@ -58,7 +72,10 @@ fn open_pause_menu(
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
fn setup(mut commands: Commands, assets: Res<UIAssets>, settings: Res<SoundSettings>) {
|
||||
let sound_volume = (settings.sound_volume * 100.) as u8;
|
||||
let music_volume = (settings.music_volume * 100.) as u8;
|
||||
|
||||
commands.spawn((
|
||||
Name::new("pause-menu"),
|
||||
Node {
|
||||
@@ -73,12 +90,16 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
BackgroundColor(Color::linear_rgba(0., 0., 0., 0.6)),
|
||||
DespawnOnExit(PauseMenuState::Open),
|
||||
children![
|
||||
spawn_progress(ProgressBar::Music, 100, assets.font.clone()),
|
||||
spawn_progress(ProgressBar::Sound, 80, assets.font.clone())
|
||||
spawn_progress(ProgressBar::Music, music_volume, assets.font.clone()),
|
||||
spawn_progress(ProgressBar::Sound, sound_volume, assets.font.clone())
|
||||
],
|
||||
));
|
||||
|
||||
commands.insert_resource(PauseMenuSelection(ProgressBar::Music));
|
||||
commands.insert_resource(VolumeChangeCooldown(Timer::from_seconds(
|
||||
0.09,
|
||||
TimerMode::Repeating,
|
||||
)));
|
||||
}
|
||||
|
||||
fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundle {
|
||||
@@ -144,6 +165,7 @@ fn spawn_progress(bar: ProgressBar, value: u8, font: Handle<Font>) -> impl Bundl
|
||||
},
|
||||
TextColor(HEDZ_GREEN.into()),
|
||||
TextLayout::new_with_justify(Justify::Left).with_no_wrap(),
|
||||
VolumeValue(bar),
|
||||
),
|
||||
(
|
||||
Node {
|
||||
@@ -186,3 +208,92 @@ fn selection_changed(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_volume_display(
|
||||
settings: Res<SoundSettings>,
|
||||
mut query: Query<(&mut Text, &VolumeValue)>,
|
||||
) {
|
||||
if settings.is_changed() {
|
||||
for (mut text, volume_value) in query.iter_mut() {
|
||||
let new_value = match volume_value.0 {
|
||||
ProgressBar::Music => (settings.music_volume * 100.) as u8,
|
||||
ProgressBar::Sound => (settings.sound_volume * 100.) as u8,
|
||||
};
|
||||
text.0 = format!("{new_value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_volume_keyboard(
|
||||
selection: Res<PauseMenuSelection>,
|
||||
keyboard: Res<ButtonInput<KeyCode>>,
|
||||
mut settings: ResMut<SoundSettings>,
|
||||
mut cooldown: ResMut<VolumeChangeCooldown>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let change = if keyboard.pressed(KeyCode::ArrowLeft) {
|
||||
-0.01
|
||||
} else if keyboard.pressed(KeyCode::ArrowRight) {
|
||||
0.01
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// On first press, apply immediately and reset timer
|
||||
if keyboard.just_pressed(KeyCode::ArrowLeft) || keyboard.just_pressed(KeyCode::ArrowRight) {
|
||||
cooldown.0.reset();
|
||||
} else {
|
||||
cooldown.0.tick(time.delta());
|
||||
if !cooldown.0.just_finished() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match selection.0 {
|
||||
ProgressBar::Music => {
|
||||
settings.music_volume = (settings.music_volume + change).clamp(0.0, 1.0);
|
||||
}
|
||||
ProgressBar::Sound => {
|
||||
settings.sound_volume = (settings.sound_volume + change).clamp(0.0, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_volume_gamepad(
|
||||
selection: Res<PauseMenuSelection>,
|
||||
gamepads: Query<&Gamepad>,
|
||||
mut settings: ResMut<SoundSettings>,
|
||||
mut cooldown: ResMut<VolumeChangeCooldown>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let Some((change, just_pressed)) = gamepads.iter().find_map(|gamepad| {
|
||||
if gamepad.pressed(GamepadButton::DPadLeft) {
|
||||
Some((-0.01, gamepad.just_pressed(GamepadButton::DPadLeft)))
|
||||
} else if gamepad.pressed(GamepadButton::DPadRight) {
|
||||
Some((0.01, gamepad.just_pressed(GamepadButton::DPadRight)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// On first press, apply immediately and reset timer
|
||||
if just_pressed {
|
||||
cooldown.0.reset();
|
||||
} else {
|
||||
cooldown.0.tick(time.delta());
|
||||
if !cooldown.0.just_finished() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match selection.0 {
|
||||
ProgressBar::Music => {
|
||||
settings.music_volume = (settings.music_volume + change).clamp(0.0, 1.0);
|
||||
}
|
||||
ProgressBar::Sound => {
|
||||
settings.sound_volume = (settings.sound_volume + change).clamp(0.0, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user