From e7bdb3709370391cdb72b95931af29dfa51a4e5b Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Fri, 17 Jan 2025 12:50:46 +0200 Subject: [PATCH 1/4] feat(sceen): add pause screen --- src/screens/gameplay.rs | 7 +++---- src/screens/mod.rs | 3 +++ src/screens/pause.rs | 43 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 src/screens/pause.rs diff --git a/src/screens/gameplay.rs b/src/screens/gameplay.rs index b41de50..42bd319 100644 --- a/src/screens/gameplay.rs +++ b/src/screens/gameplay.rs @@ -21,11 +21,10 @@ pub(super) fn plugin(app: &mut App) { app.add_systems( Update, - return_to_title_screen - .run_if(in_state(Screen::Gameplay).and(input_just_pressed(KeyCode::Escape))), + pause_game.run_if(in_state(Screen::Gameplay).and(input_just_pressed(KeyCode::Escape))), ); } -fn return_to_title_screen(mut next_screen: ResMut>) { - next_screen.set(Screen::Title); +fn pause_game(mut next_screen: ResMut>) { + next_screen.set(Screen::Pause); } diff --git a/src/screens/mod.rs b/src/screens/mod.rs index df0e70d..7af5a6c 100644 --- a/src/screens/mod.rs +++ b/src/screens/mod.rs @@ -2,6 +2,7 @@ mod gameplay; mod loading; +mod pause; mod splash; mod title; @@ -16,6 +17,7 @@ pub(super) fn plugin(app: &mut App) { loading::plugin, splash::plugin, title::plugin, + pause::plugin, )); } @@ -28,4 +30,5 @@ pub enum Screen { Loading, Title, Gameplay, + Pause, } diff --git a/src/screens/pause.rs b/src/screens/pause.rs new file mode 100644 index 0000000..c726f5b --- /dev/null +++ b/src/screens/pause.rs @@ -0,0 +1,43 @@ +use bevy::{input::common_conditions::input_just_pressed, prelude::*}; + +use crate::theme::{ + events::OnPress, + widgets::{Containers, Widgets}, +}; + +use super::Screen; + +pub(super) fn plugin(app: &mut App) { + app.add_systems(OnEnter(Screen::Pause), spawn_title_screen); + app.add_systems( + Update, + return_to_game.run_if(in_state(Screen::Pause).and(input_just_pressed(KeyCode::Escape))), + ); +} + +fn spawn_title_screen(mut commands: Commands) { + commands + .ui_root() + .insert(StateScoped(Screen::Pause)) + .with_children(|parent| { + parent.button("Continue").observe(return_to_game_trigger); + parent + .button("Exit") + .observe(return_to_title_screen_trigger); + }); +} + +fn return_to_game_trigger(_trigger: Trigger, mut next_screen: ResMut>) { + next_screen.set(Screen::Gameplay); +} + +fn return_to_title_screen_trigger( + _trigger: Trigger, + mut next_screen: ResMut>, +) { + next_screen.set(Screen::Title); +} + +fn return_to_game(mut next_screen: ResMut>) { + next_screen.set(Screen::Gameplay); +} From 5d50daf76896bfac63a77c440d4ad9ec71eebcd1 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Sat, 18 Jan 2025 16:25:16 +0200 Subject: [PATCH 2/4] feat(screens): add translucent pause screen #36 --- src/maze/systems/spawn.rs | 2 -- src/player/systems/spawn.rs | 2 -- src/screens/gameplay.rs | 17 ++++++++++++++++- src/screens/mod.rs | 1 + src/screens/pause.rs | 20 +++++++++++++++++--- src/stats/systems/mod.rs | 33 ++++++++++++++++++--------------- src/stats/systems/spawn.rs | 18 +++++++----------- 7 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/maze/systems/spawn.rs b/src/maze/systems/spawn.rs index 98c889a..26b94b9 100644 --- a/src/maze/systems/spawn.rs +++ b/src/maze/systems/spawn.rs @@ -15,7 +15,6 @@ use crate::{ components::{HexMaze, MazeConfig, Tile, Wall}, resources::GlobalMazeConfig, }, - screens::Screen, theme::palette::rose_pine::RosePineDawn, }; @@ -62,7 +61,6 @@ pub fn spawn_maze( config.clone(), Transform::from_translation(Vec3::ZERO.with_y(y_offset)), Visibility::Visible, - StateScoped(Screen::Gameplay), )) .insert_if(CurrentFloor, || floor == 1) // Only floor 1 gets CurrentFloor .id(); diff --git a/src/player/systems/spawn.rs b/src/player/systems/spawn.rs index 636572e..e37aa6b 100644 --- a/src/player/systems/spawn.rs +++ b/src/player/systems/spawn.rs @@ -5,7 +5,6 @@ use crate::{ assets::{blue_material, generate_pill_mesh}, components::{CurrentPosition, Player}, }, - screens::Screen, }; use bevy::prelude::*; @@ -33,6 +32,5 @@ pub fn spawn_player( Mesh3d(meshes.add(generate_pill_mesh(player_radius, player_height / 2.))), MeshMaterial3d(materials.add(blue_material())), Transform::from_xyz(start_pos.x, y_offset, start_pos.y), - StateScoped(Screen::Gameplay), )); } diff --git a/src/screens/gameplay.rs b/src/screens/gameplay.rs index 42bd319..61582b2 100644 --- a/src/screens/gameplay.rs +++ b/src/screens/gameplay.rs @@ -8,6 +8,7 @@ use crate::{ use bevy::{input::common_conditions::input_just_pressed, prelude::*}; pub(super) fn plugin(app: &mut App) { + app.init_resource::(); app.add_systems( OnEnter(Screen::Gameplay), ( @@ -16,8 +17,14 @@ pub(super) fn plugin(app: &mut App) { spawn_hint_command, spawn_stats_command, ) - .chain(), + .chain() + .run_if(not(resource_exists::)), ); + app.add_systems(OnEnter(Screen::Gameplay), |mut commands: Commands| { + commands.insert_resource(GameplayInitialized(true)); + }); + + app.add_systems(OnEnter(Screen::Title), reset_gameplay_state); app.add_systems( Update, @@ -28,3 +35,11 @@ pub(super) fn plugin(app: &mut App) { fn pause_game(mut next_screen: ResMut>) { next_screen.set(Screen::Pause); } + +fn reset_gameplay_state(mut commands: Commands) { + commands.remove_resource::(); +} + +#[derive(Debug, Default, Reflect, Resource, DerefMut, Deref)] +#[reflect(Resource)] +pub struct GameplayInitialized(bool); diff --git a/src/screens/mod.rs b/src/screens/mod.rs index 7af5a6c..643245b 100644 --- a/src/screens/mod.rs +++ b/src/screens/mod.rs @@ -7,6 +7,7 @@ mod splash; mod title; use bevy::prelude::*; +pub use gameplay::GameplayInitialized; pub(super) fn plugin(app: &mut App) { app.init_state::(); diff --git a/src/screens/pause.rs b/src/screens/pause.rs index c726f5b..fa3caa3 100644 --- a/src/screens/pause.rs +++ b/src/screens/pause.rs @@ -2,24 +2,38 @@ use bevy::{input::common_conditions::input_just_pressed, prelude::*}; use crate::theme::{ events::OnPress, + palette::rose_pine::RosePineDawn, + prelude::ColorScheme, widgets::{Containers, Widgets}, }; use super::Screen; pub(super) fn plugin(app: &mut App) { - app.add_systems(OnEnter(Screen::Pause), spawn_title_screen); + app.add_systems(OnEnter(Screen::Pause), spawn_pause_overlay); app.add_systems( Update, return_to_game.run_if(in_state(Screen::Pause).and(input_just_pressed(KeyCode::Escape))), ); } -fn spawn_title_screen(mut commands: Commands) { +fn spawn_pause_overlay(mut commands: Commands) { commands .ui_root() - .insert(StateScoped(Screen::Pause)) + .insert(( + StateScoped(Screen::Pause), + BackgroundColor(RosePineDawn::Muted.to_color().with_alpha(0.5)), + )) .with_children(|parent| { + parent + .spawn(Node { + bottom: Val::Px(100.), + ..default() + }) + .with_children(|parent| { + parent.header("Paused"); + }); + parent.button("Continue").observe(return_to_game_trigger); parent .button("Exit") diff --git a/src/stats/systems/mod.rs b/src/stats/systems/mod.rs index 0ee4eaa..a5eddf8 100644 --- a/src/stats/systems/mod.rs +++ b/src/stats/systems/mod.rs @@ -13,23 +13,26 @@ use reset::reset_timers; use score::{update_score, update_score_display}; use total_timer::{update_total_timer, update_total_timer_display}; -use crate::screens::Screen; +use crate::screens::{GameplayInitialized, Screen}; pub(super) fn plugin(app: &mut App) { - app.add_systems(OnEnter(Screen::Gameplay), reset_timers) - .add_systems( - Update, + app.add_systems( + OnEnter(Screen::Gameplay), + reset_timers.run_if(not(resource_exists::)), + ); + app.add_systems( + Update, + ( ( - ( - update_score.before(update_floor_timer), - update_score_display, - ) - .chain(), - (update_floor_timer, update_floor_timer_display).chain(), - (update_total_timer, update_total_timer_display).chain(), - update_floor_display, - update_highest_floor_display, + update_score.before(update_floor_timer), + update_score_display, ) - .run_if(in_state(Screen::Gameplay)), - ); + .chain(), + (update_floor_timer, update_floor_timer_display).chain(), + (update_total_timer, update_total_timer_display).chain(), + update_floor_display, + update_highest_floor_display, + ) + .run_if(in_state(Screen::Gameplay)), + ); } diff --git a/src/stats/systems/spawn.rs b/src/stats/systems/spawn.rs index ca4283c..0ec6646 100644 --- a/src/stats/systems/spawn.rs +++ b/src/stats/systems/spawn.rs @@ -1,7 +1,6 @@ use bevy::prelude::*; use crate::{ - screens::Screen, stats::{ components::{ FloorDisplay, FloorTimerDisplay, HighestFloorDisplay, ScoreDisplay, TotalTimerDisplay, @@ -12,14 +11,11 @@ use crate::{ }; pub fn spawn_stats(mut commands: Commands) { - commands - .ui_stats() - .insert(StateScoped(Screen::Gameplay)) - .with_children(|parent| { - parent.stats("Floor: 1", FloorDisplay); - parent.stats("Highest Floor: 1", HighestFloorDisplay); - parent.stats("Score: 0", ScoreDisplay); - parent.stats("Floor Timer", FloorTimerDisplay); - parent.stats("Total Timer", TotalTimerDisplay); - }); + commands.ui_stats().with_children(|parent| { + parent.stats("Floor: 1", FloorDisplay); + parent.stats("Highest Floor: 1", HighestFloorDisplay); + parent.stats("Score: 0", ScoreDisplay); + parent.stats("Floor Timer", FloorTimerDisplay); + parent.stats("Total Timer", TotalTimerDisplay); + }); } From 7a4bcd81f9e73b1298cea4799db1c2ee2c472d28 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Sat, 18 Jan 2025 16:33:52 +0200 Subject: [PATCH 3/4] feat(pause): hide walls and player when paused --- src/maze/systems/mod.rs | 8 +++++++- src/maze/systems/toogle_pause.rs | 13 +++++++++++++ src/player/systems/mod.rs | 3 +++ src/player/systems/toogle_pause.rs | 13 +++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/maze/systems/toogle_pause.rs create mode 100644 src/player/systems/toogle_pause.rs diff --git a/src/maze/systems/mod.rs b/src/maze/systems/mod.rs index 8def9f0..4630a54 100644 --- a/src/maze/systems/mod.rs +++ b/src/maze/systems/mod.rs @@ -2,7 +2,13 @@ pub mod common; pub mod despawn; pub mod respawn; pub mod spawn; +mod toogle_pause; use bevy::prelude::*; +use toogle_pause::toggle_walls; -pub(super) fn plugin(_app: &mut App) {} +use crate::screens::Screen; + +pub(super) fn plugin(app: &mut App) { + app.add_systems(Update, toggle_walls.run_if(state_changed::)); +} diff --git a/src/maze/systems/toogle_pause.rs b/src/maze/systems/toogle_pause.rs new file mode 100644 index 0000000..5bc69ab --- /dev/null +++ b/src/maze/systems/toogle_pause.rs @@ -0,0 +1,13 @@ +use bevy::prelude::*; + +use crate::{maze::components::Wall, screens::Screen}; + +pub fn toggle_walls(mut query: Query<&mut Visibility, With>, state: Res>) { + for mut visibility in query.iter_mut() { + *visibility = match *state.get() { + Screen::Gameplay => Visibility::Visible, + Screen::Pause => Visibility::Hidden, + _ => *visibility, + } + } +} diff --git a/src/player/systems/mod.rs b/src/player/systems/mod.rs index 4b15cc5..0dc993f 100644 --- a/src/player/systems/mod.rs +++ b/src/player/systems/mod.rs @@ -4,6 +4,7 @@ mod movement; pub mod respawn; mod sound_effect; pub mod spawn; +mod toogle_pause; mod vertical_transition; use crate::{screens::Screen, AppSet}; @@ -11,6 +12,7 @@ use bevy::prelude::*; use input::player_input; use movement::player_movement; use sound_effect::play_movement_sound; +use toogle_pause::toggle_player; use vertical_transition::handle_floor_transition; use super::assets::PlayerAssets; @@ -30,4 +32,5 @@ pub(super) fn plugin(app: &mut App) { .chain() .run_if(in_state(Screen::Gameplay)), ); + app.add_systems(Update, toggle_player.run_if(state_changed::)); } diff --git a/src/player/systems/toogle_pause.rs b/src/player/systems/toogle_pause.rs new file mode 100644 index 0000000..fcee999 --- /dev/null +++ b/src/player/systems/toogle_pause.rs @@ -0,0 +1,13 @@ +use bevy::prelude::*; + +use crate::{player::components::Player, screens::Screen}; + +pub fn toggle_player(mut query: Query<&mut Visibility, With>, state: Res>) { + for mut visibility in query.iter_mut() { + *visibility = match *state.get() { + Screen::Gameplay => Visibility::Visible, + Screen::Pause => Visibility::Hidden, + _ => *visibility, + } + } +} From 7fa567d522a7f96ad4423d7c8ec8c252866b7edd Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Sat, 18 Jan 2025 16:40:29 +0200 Subject: [PATCH 4/4] feat: add game entity cleanup --- src/maze/systems/spawn.rs | 2 ++ src/player/systems/spawn.rs | 2 ++ src/screens/gameplay.rs | 17 +++++++++++++++++ src/screens/mod.rs | 2 +- src/stats/systems/score.rs | 2 -- src/stats/systems/spawn.rs | 18 +++++++++++------- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/maze/systems/spawn.rs b/src/maze/systems/spawn.rs index 26b94b9..c8e2887 100644 --- a/src/maze/systems/spawn.rs +++ b/src/maze/systems/spawn.rs @@ -15,6 +15,7 @@ use crate::{ components::{HexMaze, MazeConfig, Tile, Wall}, resources::GlobalMazeConfig, }, + screens::GameplayElement, theme::palette::rose_pine::RosePineDawn, }; @@ -61,6 +62,7 @@ pub fn spawn_maze( config.clone(), Transform::from_translation(Vec3::ZERO.with_y(y_offset)), Visibility::Visible, + GameplayElement, )) .insert_if(CurrentFloor, || floor == 1) // Only floor 1 gets CurrentFloor .id(); diff --git a/src/player/systems/spawn.rs b/src/player/systems/spawn.rs index e37aa6b..41b9669 100644 --- a/src/player/systems/spawn.rs +++ b/src/player/systems/spawn.rs @@ -5,6 +5,7 @@ use crate::{ assets::{blue_material, generate_pill_mesh}, components::{CurrentPosition, Player}, }, + screens::GameplayElement, }; use bevy::prelude::*; @@ -32,5 +33,6 @@ pub fn spawn_player( Mesh3d(meshes.add(generate_pill_mesh(player_radius, player_height / 2.))), MeshMaterial3d(materials.add(blue_material())), Transform::from_xyz(start_pos.x, y_offset, start_pos.y), + GameplayElement, )); } diff --git a/src/screens/gameplay.rs b/src/screens/gameplay.rs index 61582b2..589847c 100644 --- a/src/screens/gameplay.rs +++ b/src/screens/gameplay.rs @@ -23,6 +23,7 @@ pub(super) fn plugin(app: &mut App) { app.add_systems(OnEnter(Screen::Gameplay), |mut commands: Commands| { commands.insert_resource(GameplayInitialized(true)); }); + app.add_systems(Update, cleanup_game.run_if(state_changed::)); app.add_systems(OnEnter(Screen::Title), reset_gameplay_state); @@ -43,3 +44,19 @@ fn reset_gameplay_state(mut commands: Commands) { #[derive(Debug, Default, Reflect, Resource, DerefMut, Deref)] #[reflect(Resource)] pub struct GameplayInitialized(bool); + +#[derive(Debug, Reflect, Component)] +#[reflect(Component)] +pub struct GameplayElement; + +fn cleanup_game( + mut commands: Commands, + query: Query>, + state: Res>, +) { + if !matches!(*state.get(), Screen::Gameplay | Screen::Pause) { + for entity in query.iter() { + commands.entity(entity).despawn_recursive(); + } + } +} diff --git a/src/screens/mod.rs b/src/screens/mod.rs index 643245b..8b0c2a8 100644 --- a/src/screens/mod.rs +++ b/src/screens/mod.rs @@ -7,7 +7,7 @@ mod splash; mod title; use bevy::prelude::*; -pub use gameplay::GameplayInitialized; +pub use gameplay::{GameplayElement, GameplayInitialized}; pub(super) fn plugin(app: &mut App) { app.init_state::(); diff --git a/src/stats/systems/score.rs b/src/stats/systems/score.rs index 9559902..6bb1864 100644 --- a/src/stats/systems/score.rs +++ b/src/stats/systems/score.rs @@ -59,8 +59,6 @@ fn calculate_score(floor_number: u8, completion_time: f32) -> usize { time_factor.max(MIN_TIME_MULTIPLIER) * TIME_BONUS_MULTIPLIER }; - dbg!(base_score * time_multiplier); - (base_score * time_multiplier) as usize } diff --git a/src/stats/systems/spawn.rs b/src/stats/systems/spawn.rs index 0ec6646..81d4b55 100644 --- a/src/stats/systems/spawn.rs +++ b/src/stats/systems/spawn.rs @@ -1,6 +1,7 @@ use bevy::prelude::*; use crate::{ + screens::GameplayElement, stats::{ components::{ FloorDisplay, FloorTimerDisplay, HighestFloorDisplay, ScoreDisplay, TotalTimerDisplay, @@ -11,11 +12,14 @@ use crate::{ }; pub fn spawn_stats(mut commands: Commands) { - commands.ui_stats().with_children(|parent| { - parent.stats("Floor: 1", FloorDisplay); - parent.stats("Highest Floor: 1", HighestFloorDisplay); - parent.stats("Score: 0", ScoreDisplay); - parent.stats("Floor Timer", FloorTimerDisplay); - parent.stats("Total Timer", TotalTimerDisplay); - }); + commands + .ui_stats() + .insert(GameplayElement) + .with_children(|parent| { + parent.stats("Floor: 1", FloorDisplay); + parent.stats("Highest Floor: 1", HighestFloorDisplay); + parent.stats("Score: 0", ScoreDisplay); + parent.stats("Floor Timer", FloorTimerDisplay); + parent.stats("Total Timer", TotalTimerDisplay); + }); }