Skip to content

Commit

Permalink
Merge pull request #67 from thombruce/feat/ui-damage
Browse files Browse the repository at this point in the history
Feat/UI damage
  • Loading branch information
thombruce authored Oct 26, 2023
2 parents c87f60f + 50cf59a commit f9cc542
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 29 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- UI damage indicators appear when a ship is hit

### Changed

- Camera scaling to 1.5, more zoomed out, see enemies on screen for longer
Expand Down
10 changes: 8 additions & 2 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ pub mod resources;

use self::{
effects::{animate::AnimatePlugin, blink::EffectsPlugin},
resources::{game_time::GameTimePlugin, state::StatePlugin},
resources::{despawn_timer::DespawnTimerPlugin, game_time::GameTimePlugin, state::StatePlugin},
};

pub struct CorePlugin;
impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app.add_plugins((GameTimePlugin, StatePlugin, EffectsPlugin, AnimatePlugin));
app.add_plugins((
GameTimePlugin,
StatePlugin,
EffectsPlugin,
AnimatePlugin,
DespawnTimerPlugin,
));
}
}
26 changes: 26 additions & 0 deletions src/core/resources/despawn_timer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use bevy::prelude::*;

use crate::core::resources::state::GameState;

#[derive(Component)]
pub struct DespawnTimer(pub Timer);

pub struct DespawnTimerPlugin;
impl Plugin for DespawnTimerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, despawn_system.run_if(in_state(GameState::Active)));
}
}

fn despawn_system(
mut commands: Commands,
time: Res<Time>,
mut query: Query<(Entity, &mut DespawnTimer)>,
) {
for (entity, mut bullet) in query.iter_mut() {
bullet.0.tick(time.delta());
if bullet.0.finished() {
commands.entity(entity).despawn();
}
}
}
1 change: 1 addition & 0 deletions src/core/resources/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod assets;
pub mod despawn_timer;
pub mod game_time;
pub mod state;
26 changes: 5 additions & 21 deletions src/ships/bullet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ use bevy_rapier2d::prelude::*;

use crate::core::resources::{
assets::{AudioAssets, SpriteAssets},
despawn_timer::DespawnTimer,
state::{ForState, GameState},
};

use super::ship::MovementSet;

#[derive(Component)]
pub struct Bullet {
pub despawn_timer: Timer,
}
pub struct Bullet;

#[derive(Event)]
pub struct BulletSpawnEvent {
Expand All @@ -34,8 +33,7 @@ impl Plugin for BulletPlugin {
.add_event::<BulletShipContactEvent>()
.add_systems(
Update,
(spawn_bullet.after(MovementSet), bullet_despawn_system)
.run_if(in_state(GameState::Active)),
(spawn_bullet.after(MovementSet)).run_if(in_state(GameState::Active)),
);
}
}
Expand Down Expand Up @@ -63,9 +61,8 @@ fn spawn_bullet(
texture: handles.bullet.clone(),
..default()
},
Bullet {
despawn_timer: Timer::from_seconds(2.0, TimerMode::Once),
},
Bullet,
DespawnTimer(Timer::from_seconds(2.0, TimerMode::Once)),
ForState {
states: GameState::IN_GAME_STATE.to_vec(),
},
Expand All @@ -81,16 +78,3 @@ fn spawn_bullet(
));
}
}

fn bullet_despawn_system(
mut commands: Commands,
time: Res<Time>,
mut query: Query<(Entity, &mut Bullet)>,
) {
for (entity, mut bullet) in query.iter_mut() {
bullet.despawn_timer.tick(time.delta());
if bullet.despawn_timer.finished() {
commands.entity(entity).despawn();
}
}
}
89 changes: 89 additions & 0 deletions src/ui/damage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use bevy::prelude::*;

use crate::{
core::resources::{assets::UiAssets, despawn_timer::DespawnTimer, state::GameState},
ships::{
bullet::BulletShipContactEvent,
ship::{AttackSet, Ship},
},
};

use super::resources::top::Top;

#[derive(Component)]
struct UiTextFadeOut;

pub struct UiDamagePlugin;
impl Plugin for UiDamagePlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(ui_spawn_damage, ui_text_fade_out)
.after(AttackSet)
.run_if(in_state(GameState::Active)),
);
}
}

fn ui_spawn_damage(
mut commands: Commands,
mut bullet_ship_contact_events: EventReader<BulletShipContactEvent>,
ship_transform: Query<&Transform, With<Ship>>,
camera_query: Query<(&Camera, &GlobalTransform)>,
ui: Res<UiAssets>,
) {
for event in bullet_ship_contact_events.iter() {
if let Ok(transform) = ship_transform.get(event.ship) {
// Use camera.world_to_viewport() and camera GlobalTransform to translate
// a world position into UI coordinates
let (camera, camera_transform) = camera_query.single();
let coords = camera
.world_to_viewport(camera_transform, transform.translation)
.unwrap();

commands.spawn((
TextBundle {
text: Text::from_section(
"100",
TextStyle {
font: ui.font.clone(),
font_size: 25.0,
color: Color::RED,
..default()
},
),
style: Style {
position_type: PositionType::Absolute,
top: Val::Px(coords.y),
left: Val::Px(coords.x),
..default()
},
..default()
},
DespawnTimer(Timer::from_seconds(0.5, TimerMode::Once)),
UiTextFadeOut,
Top(coords.y),
));
}
}
}

fn ui_text_fade_out(
time: Res<Time>,
mut text: Query<(&mut Text, &mut Style, &mut Top), With<UiTextFadeOut>>,
) {
for (mut txt, mut style, mut top) in text.iter_mut() {
// Moves the damage text upwards every frame
top.0 -= 100.0 * time.delta_seconds();
style.top = Val::Px(top.0);

// Fades the damage text out every frame
for section in txt.sections.iter_mut() {
let current_alpha = section.style.color.a();
section
.style
.color
.set_a(current_alpha - (2.0 * time.delta_seconds()));
}
}
}
12 changes: 6 additions & 6 deletions src/ui/menus/credits.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use bevy::prelude::*;

use crate::core::resources::{
assets::UiAssets,
state::{ForState, GameState},
use crate::{
core::resources::{
assets::UiAssets,
state::{ForState, GameState},
},
ui::resources::top::Top,
};

pub struct CreditsPlugin;
Expand All @@ -16,9 +19,6 @@ impl Plugin for CreditsPlugin {
#[derive(Component)]
pub struct Credits;

#[derive(Component)]
pub struct Top(pub f32);

fn setup(mut commands: Commands, ui: Res<UiAssets>) {
commands
.spawn((
Expand Down
4 changes: 4 additions & 0 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use bevy::prelude::*;

pub mod camera;
pub mod damage;
pub mod hud;
pub mod menus;
pub mod resources;

use self::{
camera::CameraPlugin,
damage::UiDamagePlugin,
hud::HudPlugin,
menus::{credits::CreditsPlugin, pause::PausePlugin, start_menu::MenuPlugin},
};
Expand All @@ -19,6 +22,7 @@ impl Plugin for UiPlugin {
CreditsPlugin,
PausePlugin,
CameraPlugin,
UiDamagePlugin,
));
}
}
1 change: 1 addition & 0 deletions src/ui/resources/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod top;
28 changes: 28 additions & 0 deletions src/ui/resources/top.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use bevy::prelude::*;

// TODO: This is actually just some arbitrary value we wanted to be able to change.
// It was originally created for credits scrolling and ported from there,
// and is now also being used in the ui/damage.rs system.
// Both systems wanted to be able to change the "top" value of a UI
// element, set as a Val::[Px, Percent, VMax, VMin, VH, VW](f32) which
// it seems we can't otherwise perform calculations on.
// So what this is, really, is a wrapper around some f32 intended to be used
// in some other UI element.
//
// We might therefore think about renaming it to something like Uif32 so that it
// could be used more generally for Top, Bottom, Left, Right, Margin, Padding, etc.
//
// But what if we wanted to change two or more such values? An entity can't have
// more than one of a single component, can it?
//
// No, but... we should create more of these and determine what's common about them,
// see if we can implement some kind of inheritence, and also generalise the system(s)
// that are responsible for converting these values into Val::* ones.
//
// Though, I'm not certain how possible that is... Are these values always in a
// Style component? And how do we determine what kind of Val to set?
//
// Perhaps 'Top'/'Uif32Top'/* should have a secondary value indicating type?
// Following this, the conversions could be handled in a single system here.
#[derive(Component)]
pub struct Top(pub f32);

0 comments on commit f9cc542

Please sign in to comment.