diff --git a/src/lib.rs b/src/lib.rs index fd22231..3d41ab5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ impl Plugin for AsteroidPlugin { widgets::PluginGameMenu, widgets::PluginGameOver, widgets::PluginGetReady, + widgets::PluginGameHud, RapierPhysicsPlugin::::pixels_per_meter(10.0), RapierDebugRenderPlugin::default(), )) @@ -48,10 +49,7 @@ impl Plugin for AsteroidPlugin { .insert_resource(AsteroidSpawner::new()) .init_resource::() .add_systems(Startup, spawn_camera) - .add_systems( - OnEnter(GameState::Playing), - (objects::spawn_player, widgets::spawn_ui), - ) + .add_systems(OnEnter(GameState::Playing), objects::spawn_player) .add_systems( FixedUpdate, ( diff --git a/src/widgets.rs b/src/widgets.rs index d485717..10b03af 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -1,3 +1,5 @@ +use std::ops::DerefMut; + use crate::{ GameState, config::{UI_BUTTON_HOVERED, UI_BUTTON_NORMAL, UI_BUTTON_PRESSED}, @@ -38,6 +40,17 @@ impl Plugin for PluginGetReady { } } +/// Plugin for the in-game HUD +pub struct PluginGameHud; + +impl Plugin for PluginGameHud { + fn build(&self, app: &mut App) { + app.add_systems(OnEnter(GameState::Playing), spawn_ui) + .add_systems(OnExit(GameState::Playing), despawn::) + .add_systems(Update, (operate_ui).run_if(in_state(GameState::Playing))); + } +} + /// Plugin for the game-over screen pub struct PluginGameOver; @@ -65,6 +78,10 @@ struct OnReadySetGo; #[derive(Component)] struct MarkerGameOver; +/// Marker for things on the HUD (the in-game UI elements) +#[derive(Component)] +struct MarkerHUD; + /// Action specifier for the game-over menu's buttons. /// /// Attach this component to a button and [`PluginGameOver`] will use it to @@ -294,8 +311,50 @@ fn handle_spacebar(input: Res>, mut game_state: ResMut, lives: Res) { + let score = score.0; + let lives = lives.0; commands.spawn(( - Text::new(format!("Score: {score:?} | Lives: {lives:?}")), - TextFont::from_font_size(25.0), + MarkerHUD, + Node { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + align_items: AlignItems::Start, + justify_content: JustifyContent::SpaceBetween, + padding: UiRect::all(Val::Px(5.0)), + ..default() + }, + children![ + ( + Text::new(format!("Score: {score}")), + TextFont::from_font_size(25.0), + TextShadow::default(), + ), + ( + Text::new(format!("Lives: {lives}")), + TextFont::from_font_size(25.0), + TextShadow::default(), + ) + ], )); } + +/// Updates the HUD with the current score & life count +/// +/// TODO: some kind of event-based thing. Touching the text nodes every frame +/// seems expensive. +fn operate_ui( + mut query: Single<(&Node, &Children), With>, + mut text_query: Query<&mut Text>, + lives: Res, + score: Res, +) { + let (_node, children) = query.deref_mut(); + let score = score.0; + let lives = lives.0; + // TODO: Something smarter than `unwrap()` + let mut score_text = text_query.get_mut(children[0]).unwrap(); + **score_text = format!("Score: {score}"); + + let mut lives_text = text_query.get_mut(children[1]).unwrap(); + **lives_text = format!("Lives: {lives}"); +}