Refactor visibility system to use component-based approach

- Introduce VisibleGameEntity component for entities needing visibility logic
- Move Z-index based visibility calculations to dedicated system
- Update both citizens and logs to use new visibility system
- Remove duplicated visibility logic from citizen movement system
- Add compute_visibility_of_game_entities system to Update phase

This improves modularity by separating visibility concerns from movement systems.
This commit is contained in:
2025-05-22 13:06:27 +01:00
parent a83f71addd
commit a245f35332
2 changed files with 51 additions and 27 deletions
+4 -21
View File
@@ -1,7 +1,7 @@
use crate::{ use crate::{
constants::*, constants::*,
game, game,
tilemap::{ChunkMap, CHUNK_SIZE}, tilemap::{ChunkMap, VisibleGameEntity, CHUNK_SIZE},
}; };
use bevy::{math::ivec3, prelude::*}; use bevy::{math::ivec3, prelude::*};
@@ -17,7 +17,7 @@ impl Citizen {
pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self { pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self {
Citizen { Citizen {
ambulatory: Ambulatory { ambulatory: Ambulatory {
walk_speed: 0., walk_speed: 8.,
run_speed: 6., run_speed: 6.,
target: None, target: None,
current_path: None, current_path: None,
@@ -189,24 +189,6 @@ pub fn citizen_movement(
} else if direction.x < 0.0 { } else if direction.x < 0.0 {
transform.scale.x = -PIXEL_RATIO; transform.scale.x = -PIXEL_RATIO;
} }
if (z_index.0 - transform.translation.z / TILE_SIZE) / 8.0 > 1.0 {
*visibility = Visibility::Hidden;
} else {
if transform.translation.z / TILE_SIZE <= z_index.0 + 1.0 {
*visibility = Visibility::Visible;
let saturation =
((z_index.0 - (transform.translation.z / TILE_SIZE) + 1.)
/ 8.0)
.clamp(0.0, 1.0);
sprite.color =
Color::hsv(194.7, saturation, 1.0 - (saturation / 2.0));
sprite.color.set_alpha(1.0 - saturation);
} else {
*visibility = Visibility::Hidden;
}
}
// Check if we've reached the next point // Check if we've reached the next point
if transform.translation.distance(next_point) < TILE_SIZE { if transform.translation.distance(next_point) < TILE_SIZE {
@@ -409,6 +391,7 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res<AssetServer>) {
let mut position = Vec3::new(x.round(), y.round(), 35.0) * TILE_SIZE; let mut position = Vec3::new(x.round(), y.round(), 35.0) * TILE_SIZE;
position.z += 0.1; position.z += 0.1;
commands.spawn(Citizen::new(&asset_server, position)); let cit = commands.spawn(Citizen::new(&asset_server, position)).id();
commands.entity(cit).insert(VisibleGameEntity);
} }
} }
+46 -5
View File
@@ -2,6 +2,7 @@ use std::sync::Mutex;
use std::time::Instant; use std::time::Instant;
use crate::constants::{ITILE_SIZE, TILE_SIZE}; use crate::constants::{ITILE_SIZE, TILE_SIZE};
use crate::game;
use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileMap}; use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileMap};
use crate::tiles::{ use crate::tiles::{
CurrentWorldSpriteState, FixtureTilePrefab, FloorTilePrefab, TerrainSpriteState, TextureIDs, CurrentWorldSpriteState, FixtureTilePrefab, FloorTilePrefab, TerrainSpriteState, TextureIDs,
@@ -365,7 +366,7 @@ fn generate_chunk_weathering_and_precipitation(// mut commands: Commands,
} }
fn generate_chunk_forrestry( fn generate_chunk_forrestry(
commands: ParallelCommands<'_, '_>, // Use ParallelCommands for parallel spawning commands: ParallelCommands<'_, '_>,
mut events: EventReader<ChunkForrestryEvent>, mut events: EventReader<ChunkForrestryEvent>,
mut tilemap: ResMut<TileMap>, mut tilemap: ResMut<TileMap>,
texture_ids: Res<TextureIDs>, texture_ids: Res<TextureIDs>,
@@ -402,10 +403,17 @@ fn generate_chunk_forrestry(
image: texture.clone(), image: texture.clone(),
..Default::default() ..Default::default()
}; };
commands.spawn(( let log = commands
.spawn((
sprite, sprite,
Transform::from_xyz(above_pos.x, above_pos.y, above_pos.z), Transform::from_xyz(
)); above_pos.x,
above_pos.y,
above_pos.z,
),
))
.id();
commands.entity(log).insert(VisibleGameEntity);
tree_positions.push(above_pos); tree_positions.push(above_pos);
} }
} }
@@ -475,6 +483,38 @@ pub fn absolute_z_to_world_z(z: f32) -> f32 {
z - Z_BELOW * ITILE_SIZE as f32 z - Z_BELOW * ITILE_SIZE as f32
} }
#[derive(Component)]
pub struct VisibleGameEntity;
pub fn compute_visibility_of_game_entities(
mut query: Query<(&Transform, &mut Visibility, &mut Sprite), With<VisibleGameEntity>>,
z_index: Res<game::ZIndex>,
) {
query
.par_iter_mut()
.for_each(|(transform, mut visibility, mut sprite)| {
// if too far away, saturation does not matter
if (z_index.0 - transform.translation.z / TILE_SIZE) / 8.0 > 1.0 {
*visibility = Visibility::Hidden;
return;
}
// if too high, saturation does not matter
if (transform.translation.z) / TILE_SIZE > z_index.0 + 1.0 {
*visibility = Visibility::Hidden;
return;
}
// if visible, calculate saturation
*visibility = Visibility::Visible;
let saturation =
((z_index.0 - (transform.translation.z / TILE_SIZE) + 1.) / 8.0).clamp(0.0, 1.0);
sprite.color = Color::hsv(194.7, saturation, 1.0 - (saturation / 2.0));
sprite.color.set_alpha(1.0 - saturation);
});
}
pub struct TilemapPlugin; pub struct TilemapPlugin;
impl Plugin for TilemapPlugin { impl Plugin for TilemapPlugin {
@@ -503,6 +543,7 @@ impl Plugin for TilemapPlugin {
) )
.chain(), .chain(),
) )
.add_systems(FixedUpdate, generate_chunks_from_algo); .add_systems(FixedUpdate, generate_chunks_from_algo)
.add_systems(Update, compute_visibility_of_game_entities);
} }
} }