diff --git a/flake.nix b/flake.nix index 999764b..34d5916 100644 --- a/flake.nix +++ b/flake.nix @@ -85,7 +85,8 @@ vs-rs = wrap vs-rs-unwrapped; in { devShells.default = craneLib.devShell { - inherit LD_LIBRARY_PATH buildInputs; + inherit LD_LIBRARY_PATH; + buildInputs = buildInputs ++ [pkgs.rust-analyzer]; }; packages = { diff --git a/vs-assets/src/plugin.rs b/vs-assets/src/plugin.rs index 5775522..c525d5e 100644 --- a/vs-assets/src/plugin.rs +++ b/vs-assets/src/plugin.rs @@ -44,6 +44,12 @@ pub struct GameAssets { pub money_texture: Handle, } +#[derive(Default, Resource)] +pub struct UiAssets { + pub health_bar: Handle, + pub health_bar_outline: Handle, +} + #[derive(Default, Resource)] pub struct Configs { pub enemy_config: Handle, @@ -53,6 +59,7 @@ pub struct Configs { pub struct GameAssetFolders { pub tiles_folder: Handle, pub rooms_folder: Handle, + pub ui_folder: Handle, pub tileset_main: Handle, pub tiles_loaded: bool, pub rooms_loaded: bool, @@ -90,6 +97,7 @@ fn start_loading( info!("Loading game asset folders"); let tiles_folder_handle = asset_server.load_folder("textures"); let rooms_folder_handle = asset_server.load_folder("rooms"); + let ui_folder_handle = asset_server.load_folder("ui"); let tileset_main = asset_server.load("tilesheet.tsx"); configs.enemy_config = asset_server.load("configs/enemies.json"); @@ -97,6 +105,7 @@ fn start_loading( let asset_folders = GameAssetFolders { tiles_folder: tiles_folder_handle, rooms_folder: rooms_folder_handle, + ui_folder: ui_folder_handle, tileset_main, ..default() }; @@ -152,6 +161,20 @@ fn setup_game_assets( commands.insert_resource(game_assets); + let health_bar = asset_server + .get_handle::("ui/health_bar.png") + .unwrap(); + let health_bar_outline = asset_server + .get_handle::("ui/health_bar_outline.png") + .unwrap(); + + let ui_assets = UiAssets { + health_bar, + health_bar_outline, + }; + + commands.insert_resource(ui_assets); + for (id, map) in rooms.iter() { info!( "Map id {} ({}x{})", diff --git a/vs-rs/assets/ui/health_bar.png b/vs-rs/assets/ui/health_bar.png new file mode 100644 index 0000000..cb8924b Binary files /dev/null and b/vs-rs/assets/ui/health_bar.png differ diff --git a/vs-rs/assets/ui/health_bar_outline.png b/vs-rs/assets/ui/health_bar_outline.png new file mode 100644 index 0000000..e905197 Binary files /dev/null and b/vs-rs/assets/ui/health_bar_outline.png differ diff --git a/vs-rs/src/debug.rs b/vs-rs/src/debug.rs index 33358cf..45cdaeb 100644 --- a/vs-rs/src/debug.rs +++ b/vs-rs/src/debug.rs @@ -52,8 +52,11 @@ fn close_on_esc( } } +#[derive(Component)] +struct CapybarasText; + fn add_enemy_count(mut commands: Commands) { - commands.spawn( + commands.spawn(( TextBundle::from_section( "Capybaras: ", TextStyle { @@ -68,7 +71,8 @@ fn add_enemy_count(mut commands: Commands) { left: Val::Px(5.0), ..default() }), - ); + CapybarasText, + )); commands.spawn(( TextBundle::from_sections([ @@ -92,7 +96,7 @@ fn add_enemy_count(mut commands: Commands) { fn update_enemy_count( enemies: Query<&Enemy, (With, Without)>, - mut text: Query<&mut Text, Without>, + mut text: Query<&mut Text, (With, Without)>, ) { if let Ok(mut text) = text.get_single_mut() { text.sections[0].value = format!("Capybaras: {}", enemies.iter().count()); diff --git a/vs-rs/src/main.rs b/vs-rs/src/main.rs index a10588f..5d1843f 100644 --- a/vs-rs/src/main.rs +++ b/vs-rs/src/main.rs @@ -24,12 +24,14 @@ mod player; mod prelude; mod stats; mod worlds; +mod ui; use crate::enemy::EnemyPlugin; use camera::CameraMovementPlugin; #[cfg(debug_assertions)] use debug::DebugPlugin; use player::PlayerPlugin; +use ui::UiPlugin; pub const FRAMERATE: f64 = 60.0; pub const FIXED_TIMESTEP: f64 = 1.0 / FRAMERATE; @@ -60,6 +62,7 @@ fn main() { .add_plugins(FrameTimeDiagnosticsPlugin) .add_plugins(SimpleTileMapPlugin) .add_plugins(input::InputPlugin) + .add_plugins(UiPlugin) .add_plugins(CameraMovementPlugin) .add_plugins(PlayerPlugin) .add_plugins(PhysicsPlugin) diff --git a/vs-rs/src/player.rs b/vs-rs/src/player.rs index 27ce147..63fac07 100644 --- a/vs-rs/src/player.rs +++ b/vs-rs/src/player.rs @@ -2,6 +2,7 @@ use crate::enemy::Enemy; use crate::input::PlayerControls; use crate::stats::*; use crate::AppState; +use crate::ui::health_bar::spawn_health_bar; use behaviors::SteerSeek; use bevy::sprite::Anchor; use bevy::{input::gamepad::GamepadSettings, prelude::*}; @@ -13,6 +14,7 @@ use std::time::Duration; use steering::SteeringBundle; use steering::SteeringTargetVec2; use vs_assets::plugin::GameAssets; +use vs_assets::plugin::UiAssets; pub struct PlayerPlugin; @@ -44,6 +46,7 @@ struct Direction(Vec2); struct PlayerBundle { player: Player, health: Health, + max_health: MaxHealth, inv_timer: PlTimer, direction: Direction, exp: Experience, @@ -55,6 +58,7 @@ impl PlayerBundle { Self { player: Player, health: Health(100), + max_health: MaxHealth(100), inv_timer: PlTimer(Timer::new(Duration::from_millis(500), TimerMode::Once)), direction: Direction(Vec2::ZERO), exp: Experience(0), @@ -63,7 +67,7 @@ impl PlayerBundle { } } -fn spawn(mut commands: Commands, assets: Res) { +fn spawn(mut commands: Commands, assets: Res, ui_assets: Res) { let player_tileset = &assets.player_tilesheet; commands @@ -112,7 +116,10 @@ fn spawn(mut commands: Commands, assets: Res) { }, )); }) - .observe(on_collision); + .observe(on_collision) + .with_children(|c| { + spawn_health_bar(c, ui_assets); + }); } fn handle_input( diff --git a/vs-rs/src/stats.rs b/vs-rs/src/stats.rs index 10aefce..b5cdc72 100644 --- a/vs-rs/src/stats.rs +++ b/vs-rs/src/stats.rs @@ -3,6 +3,9 @@ use bevy::prelude::Component; #[derive(Component, Clone, Debug)] pub struct Health(pub i64); +#[derive(Component, Clone, Debug)] +pub struct MaxHealth(pub i64); + #[derive(Component, Clone, Debug)] pub struct Damage(pub i64); diff --git a/vs-rs/src/ui.rs b/vs-rs/src/ui.rs new file mode 100644 index 0000000..3f466a7 --- /dev/null +++ b/vs-rs/src/ui.rs @@ -0,0 +1,16 @@ +use bevy::prelude::*; + +pub mod game_timer; +pub mod health_bar; + +use game_timer::GameTimerPlugin; +use health_bar::HealthBarPlugin; + +pub struct UiPlugin; + +impl Plugin for UiPlugin { + fn build(&self, app: &mut App) { + app.add_plugins(GameTimerPlugin) + .add_plugins(HealthBarPlugin); + } +} diff --git a/vs-rs/src/ui/game_timer.rs b/vs-rs/src/ui/game_timer.rs new file mode 100644 index 0000000..7bf8fca --- /dev/null +++ b/vs-rs/src/ui/game_timer.rs @@ -0,0 +1,61 @@ +use bevy::prelude::*; +use bevy::text::*; +use bevy::time::Stopwatch; +use std::time::Duration; +use bevy::sprite::Anchor; + +pub struct GameTimerPlugin; + +impl Plugin for GameTimerPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Startup, spawn) + .insert_resource(GameTimer(Stopwatch::default())) + .add_systems(Update, update_timer); + } +} + +#[derive(Resource)] +struct GameTimer(Stopwatch); + +#[derive(Component)] +struct GameTimerText; + +fn spawn (mut commands: Commands) { + commands.spawn(NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, + justify_self: JustifySelf::Center, + ..Default::default() + }, + z_index: ZIndex::Global(2), + ..Default::default() + }).with_children(|parent| { + parent.spawn(( + TextBundle::from_section( + "00:00", + TextStyle { + font_size: 50.0, + color: Color::BLACK, + ..default() + }, + ).with_text_justify(JustifyText::Center), + GameTimerText, + )); + }); +} + +fn update_timer( + time: Res