feat: Implement Game Over state and UI cleanup
This commit is contained in:
parent
9e5addb59d
commit
0f4737fffd
|
@ -43,10 +43,10 @@ nix develop --command bash -c "cargo build"
|
|||
* ~~Add a `PlayerLives` resource (e.g., starting with 3).~~
|
||||
* ~~Modify `check_player_enemy_collisions`: Instead of just printing, decrement the lives count.~~
|
||||
* ~~Implement player destruction (despawn the player sprite) and respawn logic (maybe after a short delay, with temporary invincibility).~~
|
||||
* **Game Over State:**
|
||||
* Use Bevy's `States` (e.g., `Playing`, `GameOver`).
|
||||
* Transition to `GameOver` when `PlayerLives` reaches zero.
|
||||
* In the `GameOver` state: stop enemy spawning, stop player controls, display a "Game Over" message (using `bevy_ui`), potentially offer a restart option.
|
||||
* ~~**Game Over State:**~~ **(DONE)**
|
||||
* ~~Use Bevy's `States` (e.g., `Playing`, `GameOver`).~~
|
||||
* ~~Transition to `GameOver` when `PlayerLives` reaches zero.~~
|
||||
* ~~In the `GameOver` state: stop enemy spawning, stop player controls, display a "Game Over" message (using `bevy_ui`), potentially offer a restart option.~~
|
||||
* **Scoring:**
|
||||
* Add a `Score` resource.
|
||||
* Increment the score in `check_bullet_collisions` when an enemy is hit.
|
||||
|
|
93
src/main.rs
93
src/main.rs
|
@ -1,6 +1,12 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy::prelude::*; // Removed unused AppExit
|
||||
use std::time::Duration;
|
||||
|
||||
// --- Game States ---
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
|
||||
enum AppState {
|
||||
#[default]
|
||||
Playing,
|
||||
GameOver,
|
||||
}
|
||||
// --- Constants ---
|
||||
const WINDOW_WIDTH: f32 = 600.0;
|
||||
const WINDOW_HEIGHT: f32 = 800.0;
|
||||
|
@ -53,9 +59,65 @@ struct PlayerRespawnTimer {
|
|||
timer: Timer,
|
||||
}
|
||||
|
||||
// --- Game Over UI ---
|
||||
|
||||
#[derive(Component)]
|
||||
struct GameOverUI;
|
||||
|
||||
fn setup_game_over_ui(mut commands: Commands) {
|
||||
println!("Entering GameOver state. Setting up UI.");
|
||||
commands.spawn((
|
||||
TextBundle::from_section(
|
||||
"GAME OVER",
|
||||
TextStyle {
|
||||
font_size: 100.0,
|
||||
color: Color::WHITE,
|
||||
..default()
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
align_self: AlignSelf::Center,
|
||||
justify_self: JustifySelf::Center,
|
||||
top: Val::Percent(40.0), // Center vertically roughly
|
||||
..default()
|
||||
}),
|
||||
GameOverUI, // Tag the UI element
|
||||
));
|
||||
// TODO: Add "Press R to Restart" text later
|
||||
}
|
||||
|
||||
fn cleanup_game_over_ui(mut commands: Commands, query: Query<Entity, With<GameOverUI>>) {
|
||||
println!("Exiting GameOver state. Cleaning up UI.");
|
||||
for entity in query.iter() {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cleanup ---
|
||||
|
||||
fn cleanup_game_entities(
|
||||
mut commands: Commands,
|
||||
bullet_query: Query<Entity, With<Bullet>>,
|
||||
enemy_query: Query<Entity, With<Enemy>>,
|
||||
// Optionally despawn player too, or handle separately if needed for restart
|
||||
// player_query: Query<Entity, With<Player>>,
|
||||
) {
|
||||
println!("Exiting Playing state. Cleaning up game entities.");
|
||||
for entity in bullet_query.iter() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
for entity in enemy_query.iter() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
// for entity in player_query.iter() {
|
||||
// commands.entity(entity).despawn();
|
||||
// }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
App::new() // Start App builder
|
||||
.init_state::<AppState>() // Initialize the AppState *after* App::new()
|
||||
.insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
|
@ -76,19 +138,29 @@ fn main() {
|
|||
})
|
||||
// Add Systems
|
||||
.add_systems(Startup, setup)
|
||||
// Systems running only when Playing
|
||||
.add_systems(Update, (
|
||||
move_player,
|
||||
player_shoot.run_if(player_exists), // Only shoot if player exists
|
||||
player_shoot.run_if(player_exists),
|
||||
move_bullets,
|
||||
move_enemies,
|
||||
spawn_enemies,
|
||||
check_bullet_collisions,
|
||||
check_player_enemy_collisions.run_if(player_vulnerable), // Only check if player exists and is not invincible
|
||||
respawn_player.run_if(should_respawn_player), // Conditionally run respawn
|
||||
manage_invincibility, // Handle invincibility timer and blinking
|
||||
update_window_title, // Show lives in window title
|
||||
// (Game Over check would go here later)
|
||||
check_player_enemy_collisions.run_if(player_vulnerable),
|
||||
respawn_player.run_if(should_respawn_player),
|
||||
manage_invincibility,
|
||||
// Game Over check is now implicit in check_player_enemy_collisions
|
||||
).run_if(in_state(AppState::Playing)))
|
||||
// Systems running regardless of state (or managing state transitions)
|
||||
.add_systems(Update, (
|
||||
update_window_title, // Keep title updated
|
||||
// Add system to check for restart input in GameOver state later
|
||||
bevy::window::close_on_esc, // Allow closing anytime
|
||||
))
|
||||
// Systems for entering/exiting states
|
||||
.add_systems(OnEnter(AppState::GameOver), setup_game_over_ui)
|
||||
.add_systems(OnExit(AppState::GameOver), cleanup_game_over_ui)
|
||||
.add_systems(OnExit(AppState::Playing), cleanup_game_entities) // Cleanup when leaving Playing
|
||||
.run();
|
||||
}
|
||||
|
||||
|
@ -275,6 +347,7 @@ fn check_player_enemy_collisions(
|
|||
mut commands: Commands,
|
||||
mut lives: ResMut<PlayerLives>,
|
||||
mut respawn_timer: ResMut<PlayerRespawnTimer>,
|
||||
mut next_state: ResMut<NextState<AppState>>, // Resource to change state
|
||||
// Query player without Invincible component - relies on run_if condition too
|
||||
player_query: Query<(Entity, &Transform), (With<Player>, Without<Invincible>)>,
|
||||
enemy_query: Query<(Entity, &Transform), With<Enemy>>,
|
||||
|
@ -300,7 +373,7 @@ fn check_player_enemy_collisions(
|
|||
println!("Respawn timer started.");
|
||||
} else {
|
||||
println!("GAME OVER!");
|
||||
// Game Over logic would go here - e.g., transition to a GameOver state
|
||||
next_state.set(AppState::GameOver); // Transition to GameOver state
|
||||
}
|
||||
// Important: Break after handling one collision per frame for the player
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue