diff --git a/src/configuration.rs b/src/configuration.rs index 5ec5422..7f90877 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -23,6 +23,40 @@ pub struct BarSettings { pub phantom_data: PhantomData, } +impl BarSettings { + fn absolute_height(&self) -> f32 { + match self.height { + BarHeight::Relative(pct) => pct * self.width, + BarHeight::Static(height) => height, + } + } + + pub fn normalized_height(&self) -> f32 { + match self.orientation { + BarOrientation::Horizontal => self.absolute_height(), + BarOrientation::Vertical => self.width, + } + } + + pub fn normalized_width(&self) -> f32 { + match self.orientation { + BarOrientation::Horizontal => self.width, + BarOrientation::Vertical => self.absolute_height(), + } + } + + fn offset_axis(&self) -> Vec3 { + match self.orientation { + BarOrientation::Horizontal => Vec3::Y, + BarOrientation::Vertical => Vec3::X, + } + } + + pub fn normalized_offset(&self) -> Vec3 { + self.offset * self.offset_axis() + } +} + impl Default for BarSettings { fn default() -> Self { Self { diff --git a/src/plugin.rs b/src/plugin.rs index c16bf5e..dc2762a 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -4,7 +4,7 @@ use bevy::asset::load_internal_asset; use bevy::pbr::{NotShadowCaster, NotShadowReceiver}; use bevy::prelude::*; -use crate::configuration::{BarHeight, ForegroundColor, Percentage}; +use crate::configuration::{ForegroundColor, Percentage}; use crate::constants::BAR_SHADER_HANDLE; use crate::material::BarMaterial; use crate::mesh::MeshHandles; @@ -39,7 +39,10 @@ impl Plugin for HealthBarPlugin { .init_resource::>() .register_type::>() .add_systems(PostUpdate, reset_rotation) - .add_systems(Update, (spawn::, remove::, update::)); + .add_systems( + Update, + (spawn::, remove::, update::, update_settings::), + ); } } @@ -61,81 +64,59 @@ fn spawn( color_scheme: Res>, query: Query<(Entity, &T, &BarSettings), Added>, ) { - query.iter().for_each( - |( - entity, - percentage, - BarSettings { + query.iter().for_each(|(entity, percentage, settings)| { + let width = settings.normalized_width(); + let height = settings.normalized_height(); + + let mesh = mesh_handles.get(width, height).cloned().unwrap_or_else(|| { + mesh_handles.insert( width, height, - offset, - border, - orientation, - .. - }, - )| { - let height = match height { - BarHeight::Relative(pct) => pct * width, - BarHeight::Static(height) => *height, - }; - - let (width, height, vertical, offset_axis) = match orientation { - BarOrientation::Horizontal => (*width, height, false, Vec3::Y), - BarOrientation::Vertical => (height, *width, true, Vec3::X), - }; - - let mesh = mesh_handles.get(width, height).cloned().unwrap_or_else(|| { - mesh_handles.insert( - width, - height, - meshes.add(Mesh::from(shape::Quad::new(Vec2::new(width, height)))), - ) - }); - - let offset = *offset * offset_axis; - - let (high, moderate, low) = match color_scheme.foreground_color { - ForegroundColor::Static(color) => (color, color, color), - ForegroundColor::TriSpectrum { - high, - moderate, - low, - } => (high, moderate, low), - }; - - let material = materials.add(BarMaterial { - value_and_dimensions: (percentage.value(), width, height, border.width).into(), - background_color: color_scheme.background_color, - high_color: high, - moderate_color: moderate, - low_color: low, - vertical, - offset: offset.extend(0.), - border_color: border.color, - }); - - let health_bar = commands - .spawn(( - Name::new(format!("{}Bar", T::type_path())), - MaterialMeshBundle { - mesh, - material, - ..default() - }, - NotShadowCaster, - NotShadowReceiver, - )) - .id(); - - commands - .entity(entity) - .insert(WithBar(health_bar, PhantomData::)) - .add_child(health_bar); - }, - ); + meshes.add(Mesh::from(shape::Quad::new(Vec2::new(width, height)))), + ) + }); + + let (high, moderate, low) = match color_scheme.foreground_color { + ForegroundColor::Static(color) => (color, color, color), + ForegroundColor::TriSpectrum { + high, + moderate, + low, + } => (high, moderate, low), + }; + + let material = materials.add(BarMaterial { + value_and_dimensions: (percentage.value(), width, height, settings.border.width).into(), + background_color: color_scheme.background_color, + high_color: high, + moderate_color: moderate, + low_color: low, + vertical: settings.orientation == BarOrientation::Vertical, + offset: settings.normalized_offset().extend(0.), + border_color: settings.border.color, + }); + + let health_bar = commands + .spawn(( + Name::new(format!("{}Bar", T::type_path())), + MaterialMeshBundle { + mesh, + material, + ..default() + }, + NotShadowCaster, + NotShadowReceiver, + )) + .id(); + + commands + .entity(entity) + .insert(WithBar(health_bar, PhantomData::)) + .add_child(health_bar); + }); } -fn update( +fn update( mut materials: ResMut>, parent_query: Query<(&WithBar, &T), Changed>, bar_query: Query<&Handle>, @@ -149,6 +130,34 @@ fn update( }); } +#[allow(clippy::type_complexity)] +fn update_settings( + mut materials: ResMut>, + parent_query: Query<(&WithBar, &BarSettings), Changed>>, + bar_query: Query<&Handle>, +) { + parent_query.iter().for_each(|(bar, settings)| { + let Ok(material_handle) = bar_query.get(bar.get()) else { + return; + }; + + let material = materials.get_mut(material_handle).unwrap(); + let offset = settings.normalized_offset().extend(0.); + + if material.offset != offset { + material.offset = offset + } + + if material.border_color != settings.border.color { + material.border_color = settings.border.color + } + + if material.value_and_dimensions.w != settings.border.width { + material.value_and_dimensions.w = settings.border.width + } + }); +} + fn remove( mut commands: Commands, mut removals: RemovedComponents,