Tilemap chunking part 2

This commit is contained in:
StephenAdamson
2025-01-14 23:52:09 +00:00
parent c88067437c
commit 7f1ec77ab0
6 changed files with 222 additions and 209 deletions
+2 -2
View File
@@ -64,9 +64,9 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res<AssetServer>) {
// Spawn a handful of citizens // Spawn a handful of citizens
for _ in 0..10 { for _ in 0..10 {
let x = rng.gen_range(-10.0..10.0); let x = rng.gen_range(-15.0..10.0);
let y = rng.gen_range(-10.0..10.0); let y = rng.gen_range(-10.0..10.0);
let position = Vec3::new(x, y, 0.0) * PIXEL_RATIO; let position = Vec3::new(x, y, 0.1) * TILE_SIZE;
commands.spawn(Citizen::new(&asset_server, position)); commands.spawn(Citizen::new(&asset_server, position));
} }
+1 -1
View File
@@ -1,2 +1,2 @@
pub const PIXEL_RATIO: f32 = 1.; pub const PIXEL_RATIO: f32 = 2.;
pub const TILE_SIZE: f32 = 16.0 * PIXEL_RATIO; pub const TILE_SIZE: f32 = 16.0 * PIXEL_RATIO;
+10 -12
View File
@@ -1,4 +1,3 @@
use bevy::prelude::*; use bevy::prelude::*;
mod camera; mod camera;
@@ -27,7 +26,6 @@ fn main() {
) )
.insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0)))
.add_plugins(tilemap::TilemapPlugin) .add_plugins(tilemap::TilemapPlugin)
.add_plugins(tilemap::OcclusionPlugin)
.add_systems( .add_systems(
Startup, Startup,
( (
@@ -61,14 +59,14 @@ fn main() {
fn setup_initial_chunks(mut event_writer: EventWriter<tilemap::LoadChunkEvent>) { fn setup_initial_chunks(mut event_writer: EventWriter<tilemap::LoadChunkEvent>) {
println!("setup_initial_chunks"); println!("setup_initial_chunks");
// Spawn a 3x3 grid of chunks around the origin // // Spawn a 3x3 grid of chunks around the origin
for x in -1..=1 { // for x in -5..=5 {
for y in -1..=1 { // for y in -5..=5 {
event_writer.send(tilemap::LoadChunkEvent { // event_writer.send(tilemap::LoadChunkEvent {
chunk_position: IVec2::new(x, y), // chunk_position: IVec2::new(x, y),
}); // });
} // }
} // }
} }
// New system to check camera position and load nearby chunks // New system to check camera position and load nearby chunks
@@ -87,8 +85,8 @@ fn check_camera_position_for_chunks(
(camera_pos.y / (tilemap::CHUNK_SIZE as f32 * constants::TILE_SIZE)).floor() as i32; (camera_pos.y / (tilemap::CHUNK_SIZE as f32 * constants::TILE_SIZE)).floor() as i32;
// Check a 3x3 area around the camera's current chunk // Check a 3x3 area around the camera's current chunk
for x in (chunk_x - 1)..=(chunk_x + 1) { for x in (chunk_x - 2)..=(chunk_x + 2) {
for y in (chunk_y - 1)..=(chunk_y + 1) { for y in (chunk_y - 2)..=(chunk_y + 2) {
let chunk_pos = IVec2::new(x, y); let chunk_pos = IVec2::new(x, y);
// Only spawn new chunks if they haven't been loaded yet // Only spawn new chunks if they haven't been loaded yet
+8 -4
View File
@@ -34,6 +34,11 @@ pub struct FixtureTile {
pub visible_range: [u32; 8], pub visible_range: [u32; 8],
} }
#[derive(Component)]
pub struct NeedsOccluded {
pub has_been_occluded: bool,
}
impl Default for FixtureTile { impl Default for FixtureTile {
fn default() -> Self { fn default() -> Self {
Self { Self {
@@ -68,7 +73,7 @@ pub fn camera_z_movement(
} }
} }
#[derive(Resource, Default)] #[derive(Resource, Default, Clone)]
pub struct TileMap { pub struct TileMap {
pub floors: HashMap<IVec3, (u32, bool, bool, u8, [u32; 8])>, pub floors: HashMap<IVec3, (u32, bool, bool, u8, [u32; 8])>,
pub fixtures: HashMap<IVec3, (u32, bool, [u32; 8])>, pub fixtures: HashMap<IVec3, (u32, bool, [u32; 8])>,
@@ -77,11 +82,10 @@ pub struct TileMap {
pub fn update_tile_visibility( pub fn update_tile_visibility(
z_index: ResMut<game::ZIndex>, z_index: ResMut<game::ZIndex>,
mut query_set: ParamSet<( mut query_set: ParamSet<(
Query<(&FloorTile, &mut Visibility)>, Query<(&FloorTile, &mut Visibility), Without<NeedsOccluded>>,
Query<(&FixtureTile, &mut Visibility)>, Query<(&FixtureTile, &mut Visibility), Without<NeedsOccluded>>,
)>, )>,
) { ) {
let z_index = (z_index.0 as i32 + 150) as usize; let z_index = (z_index.0 as i32 + 150) as usize;
// Update floor visibility // Update floor visibility
+78 -132
View File
@@ -1,5 +1,5 @@
use crate::constants::TILE_SIZE; use crate::constants::TILE_SIZE;
use crate::tile::{FixtureTile, FloorTile, TileMap}; use crate::tile::{self, FixtureTile, FloorTile, NeedsOccluded, TileMap};
use crate::tiles::{FixtureTilePrefab, FloorTilePrefab}; use crate::tiles::{FixtureTilePrefab, FloorTilePrefab};
use bevy::prelude::*; use bevy::prelude::*;
use noise::{NoiseFn, Perlin}; use noise::{NoiseFn, Perlin};
@@ -30,30 +30,50 @@ fn setup_chunk_system(mut commands: Commands) {
commands.insert_resource(ChunkMap::default()); commands.insert_resource(ChunkMap::default());
} }
// Events
#[derive(Event)]
pub struct ChunkOcclusionEvent {
pub chunk_pos: IVec2,
}
#[derive(Event)]
pub struct TileOcclusionUpdateEvent {
pub positions: Vec<IVec3>,
}
// System to handle individual tile occlusion updates // System to handle individual tile occlusion updates
fn handle_tile_occlusion_updates( pub fn handle_tile_occlusion_updates(
mut events: EventReader<TileOcclusionUpdateEvent>, mut commands: Commands,
tilemap: Res<TileMap>, tilemap: Res<TileMap>,
mut query_set: ParamSet<( mut query_set: ParamSet<(
Query<(&mut FloorTile, &Transform)>, Query<(&mut FloorTile, &Transform, &mut NeedsOccluded)>,
Query<(&mut FixtureTile, &Transform)>, Query<(&mut FixtureTile, &Transform, &mut NeedsOccluded)>,
Query<(Entity, &mut NeedsOccluded)>,
)>, )>,
) { ) {
query_set
.p0()
.par_iter_mut()
.for_each(|(mut tile, transform, mut needs_occluded)| {
if needs_occluded.has_been_occluded {
return;
}
tile.visible_range = calculate_visibility(transform.translation.as_ivec3(), &tilemap);
needs_occluded.has_been_occluded = true;
});
query_set
.p1()
.par_iter_mut()
.for_each(|(mut fixture, transform, mut needs_occluded)| {
if needs_occluded.has_been_occluded {
return;
}
fixture.visible_range = calculate_visibility(
transform.translation.as_ivec3() - IVec3::new(0, 0, 1),
&tilemap,
);
needs_occluded.has_been_occluded = true;
});
for (entity, occ) in query_set.p2().iter_mut() {
if occ.has_been_occluded {
commands.entity(entity).remove::<NeedsOccluded>();
}
}
}
pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] {
let tile_size = TILE_SIZE as i32; let tile_size = TILE_SIZE as i32;
let calculate_visibility = |pos: IVec3, tilemap: &TileMap| {
let mut visible_range = [0u32; 8]; let mut visible_range = [0u32; 8];
for mut camera_z in -150..100 { for mut camera_z in -150..100 {
@@ -69,19 +89,18 @@ fn handle_tile_occlusion_updates(
'vertical_check: for z_offset in 1..=35 { 'vertical_check: for z_offset in 1..=35 {
let above_pos = IVec3::new(pos.x, pos.y, pos.z + (z_offset * tile_size)); let above_pos = IVec3::new(pos.x, pos.y, pos.z + (z_offset * tile_size));
if above_pos.z <= camera_z { if above_pos.z <= camera_z {
// Check both floors and fixtures for occlusion
if let Some(&(_, opaque, _, _, _)) = tilemap.floors.get(&above_pos) { if let Some(&(_, opaque, _, _, _)) = tilemap.floors.get(&above_pos) {
if opaque { if opaque {
is_occluded = true; is_occluded = true;
break 'vertical_check; break 'vertical_check;
} }
} }
if let Some(&(_, solid, _)) = tilemap.fixtures.get(&above_pos) { // if let Some(&(_, solid, _)) = tilemap.fixtures.get(&above_pos) {
if solid { // if solid {
is_occluded = true; // is_occluded = true;
break 'vertical_check; // break 'vertical_check;
} // }
} // }
} else { } else {
break 'vertical_check; break 'vertical_check;
} }
@@ -99,57 +118,30 @@ fn handle_tile_occlusion_updates(
pos.z + z_offset * tile_size, pos.z + z_offset * tile_size,
); );
// Check both floors and fixtures for visibility
if let Some(&(id, _, _, _, _)) = tilemap.floors.get(&neighbor_pos) { if let Some(&(id, _, _, _, _)) = tilemap.floors.get(&neighbor_pos) {
if id == 0 { if id == 0 {
is_visible = true; is_visible = true;
break 'neighbor_check; break 'neighbor_check;
} }
} }
if let Some(&(id, _, _)) = tilemap.fixtures.get(&neighbor_pos) { // if let Some(&(id, _, _)) = tilemap.fixtures.get(&neighbor_pos) {
if id == 0 { // if id == 0 {
is_visible = true; // is_visible = true;
break 'neighbor_check; // break 'neighbor_check;
} // }
} // }
} }
} }
} }
} }
if is_visible { if is_visible {
print!("#");
let z2 = ((camera_z / tile_size) + 150) as usize; let z2 = ((camera_z / tile_size) + 150) as usize;
visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32); visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32);
} }
} }
visible_range visible_range
};
for event in events.read() {
print!("event2");
// Process floor tiles
for (mut tile, transform) in query_set.p0().iter_mut() {
let pos = transform.translation.as_ivec3();
if event.positions.contains(&pos) {
// this never calls
print!("floor");
tile.visible_range = calculate_visibility(pos, &tilemap);
}
}
// Process fixture tiles
for (mut fixture, transform) in query_set.p1().iter_mut() {
let pos = transform.translation.as_ivec3();
if event.positions.contains(&pos) {
// this never calls
print!("fixture");
let adjusted_pos = pos - IVec3::new(0, 0, 1);
fixture.visible_range = calculate_visibility(adjusted_pos, &tilemap);
}
}
}
} }
fn handle_chunk_loading( fn handle_chunk_loading(
@@ -158,11 +150,11 @@ fn handle_chunk_loading(
mut events: EventReader<LoadChunkEvent>, mut events: EventReader<LoadChunkEvent>,
mut chunk_map: ResMut<ChunkMap>, mut chunk_map: ResMut<ChunkMap>,
mut tilemap: ResMut<TileMap>, mut tilemap: ResMut<TileMap>,
mut occlusion_events: EventWriter<ChunkOcclusionEvent>,
) { ) {
let noise = Perlin::new(0); let noise = Perlin::new(0);
for event in events.read() { for event in events.read() {
println!("Loading chunk {}", event.chunk_position);
let chunk_pos = event.chunk_position; let chunk_pos = event.chunk_position;
if chunk_map.loaded_chunks.contains_key(&chunk_pos) { if chunk_map.loaded_chunks.contains_key(&chunk_pos) {
continue; continue;
@@ -181,8 +173,10 @@ fn handle_chunk_loading(
let world_x = start_x + local_x; let world_x = start_x + local_x;
let world_y = start_y + local_y; let world_y = start_y + local_y;
let noise_value_a = noise.get([world_x as f64 * 0.01, world_y as f64 * 0.01]) * 1.25; let noise_value_a =
let noise_value_b = noise.get([world_x as f64 * 0.05, world_y as f64 * 0.05]) * 0.25; noise.get([world_x as f64 * 0.01, world_y as f64 * 0.01]) * 1.25;
let noise_value_b =
noise.get([world_x as f64 * 0.05, world_y as f64 * 0.05]) * 0.25;
let combined_noise_value = noise_value_a + noise_value_b; let combined_noise_value = noise_value_a + noise_value_b;
let noise_position = Vec3::new( let noise_position = Vec3::new(
@@ -201,33 +195,39 @@ fn handle_chunk_loading(
let pos_ivec = position.as_ivec3(); let pos_ivec = position.as_ivec3();
if noise_position.z > position.z { if noise_position.z > position.z {
if z < -15 { if z < -8 {
let noise_value = noise.get([world_x as f64 * 0.05, world_y as f64 * 0.05, z as f64 * 0.05]); let noise_value = noise.get([
world_x as f64 * 0.05,
world_y as f64 * 0.05,
z as f64 * 0.05,
]);
if noise_value < -0.5 { if noise_value < -0.5 {
commands.spawn(FloorTilePrefab::air(position, &asset_server)); FloorTilePrefab::air(position, &asset_server).spawn(&mut commands);
tilemap.floors.insert(pos_ivec, (0, false, true, 1, [0; 8])); // Air tile tilemap.floors.insert(pos_ivec, (0, false, true, 1, [0; 8])); // Air tile
floor_positions.push((position, "air")); floor_positions.push((position, "air"));
} else if noise_value < 0.8 { } else if noise_value < 0.8 {
commands.spawn(FloorTilePrefab::rock(position, &asset_server)); FloorTilePrefab::rock(position, &asset_server).spawn(&mut commands);
tilemap.floors.insert(pos_ivec, (2, true, false, 255, [0; 8])); // Rock tile tilemap
.floors
.insert(pos_ivec, (2, true, false, 255, [0; 8])); // Rock tile
floor_positions.push((position, "rock")); floor_positions.push((position, "rock"));
} else { } else {
commands.spawn(FloorTilePrefab::dirt(position, &asset_server)); FloorTilePrefab::dirt(position, &asset_server).spawn(&mut commands);
tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile
floor_positions.push((position, "dirt")); floor_positions.push((position, "dirt"));
} }
} else { } else {
commands.spawn(FloorTilePrefab::dirt(position, &asset_server)); FloorTilePrefab::dirt(position, &asset_server).spawn(&mut commands);
tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile
floor_positions.push((position, "dirt")); floor_positions.push((position, "dirt"));
} }
} else if noise_position.z < position.z { } else if noise_position.z < position.z {
commands.spawn(FloorTilePrefab::air(position, &asset_server)); FloorTilePrefab::air(position, &asset_server).spawn(&mut commands);
tilemap.floors.insert(pos_ivec, (0, false, true, 1, [0; 8])); // Air tile tilemap.floors.insert(pos_ivec, (0, false, true, 1, [0; 8])); // Air tile
floor_positions.push((position, "air")); floor_positions.push((position, "air"));
} else { } else {
commands.spawn(FloorTilePrefab::dirt(position, &asset_server)); FloorTilePrefab::grass(position, &asset_server).spawn(&mut commands);
tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Grass tile
floor_positions.push((position, "dirt")); floor_positions.push((position, "dirt"));
} }
} }
@@ -241,62 +241,20 @@ fn handle_chunk_loading(
match *floor_type { match *floor_type {
"dirt" => { "dirt" => {
commands.spawn(FixtureTilePrefab::dirt_wall(above_pos, &asset_server)); FixtureTilePrefab::dirt_wall(above_pos, &asset_server).spawn(&mut commands);
tilemap.fixtures.insert(above_ivec, (1, true, [0; 8])); // Dirt wall tilemap.fixtures.insert(above_ivec, (1, true, [0; 8])); // Dirt wall
} }
"rock" => { "rock" => {
commands.spawn(FixtureTilePrefab::rock_wall(above_pos, &asset_server)); FixtureTilePrefab::rock_wall(above_pos, &asset_server).spawn(&mut commands);
tilemap.fixtures.insert(above_ivec, (2, true, [0; 8])); // Rock wall tilemap.fixtures.insert(above_ivec, (2, true, [0; 8])); // Rock wall
} }
"bedrock" => { "bedrock" => {
commands.spawn(FixtureTilePrefab::bedrock_wall(above_pos, &asset_server)); FixtureTilePrefab::bedrock_wall(above_pos, &asset_server).spawn(&mut commands);
tilemap.fixtures.insert(above_ivec, (3, true, [0; 8])); // Bedrock wall tilemap.fixtures.insert(above_ivec, (3, true, [0; 8])); // Bedrock wall
} }
_ => {} _ => {}
} }
} }
// Emit occlusion event for this chunk
occlusion_events.send(ChunkOcclusionEvent {
chunk_pos,
});
}
}
// System to handle chunk occlusion events
fn handle_chunk_occlusion_events(
mut events: EventReader<ChunkOcclusionEvent>,
tilemap: Res<TileMap>,
mut tile_update_events: EventWriter<TileOcclusionUpdateEvent>,
) {
for event in events.read() {
let chunk_pos = event.chunk_pos;
let tile_size = TILE_SIZE as i32;
let start_x = chunk_pos.x * CHUNK_SIZE * tile_size;
let end_x = start_x + (CHUNK_SIZE * tile_size);
let start_y = chunk_pos.y * CHUNK_SIZE * tile_size;
let end_y = start_y + (CHUNK_SIZE * tile_size);
// Collect positions that need visibility updates
let mut positions = Vec::new();
// Add positions from the current chunk
for x in (start_x..end_x).step_by(tile_size as usize) {
for y in (start_y..end_y).step_by(tile_size as usize) {
for z in -150..50 {
let pos = IVec3::new(y, x, z * tile_size);
if tilemap.floors.contains_key(&pos) || tilemap.fixtures.contains_key(&pos) {
positions.push(pos);
}
}
}
}
// Send update event if we have positions to process
if !positions.is_empty() {
tile_update_events.send(TileOcclusionUpdateEvent { positions });
}
} }
} }
@@ -307,21 +265,9 @@ impl Plugin for TilemapPlugin {
app.init_resource::<ChunkMap>() app.init_resource::<ChunkMap>()
.add_event::<LoadChunkEvent>() .add_event::<LoadChunkEvent>()
.add_systems(Startup, setup_chunk_system) .add_systems(Startup, setup_chunk_system)
.add_systems(Update, handle_chunk_loading); .add_systems(
} FixedUpdate,
} (handle_chunk_loading, handle_tile_occlusion_updates).chain(),
);
// Plugin to handle occlusion systems
pub struct OcclusionPlugin;
impl Plugin for OcclusionPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ChunkOcclusionEvent>()
.add_event::<TileOcclusionUpdateEvent>()
.add_systems(Update, (
handle_chunk_occlusion_events,
handle_tile_occlusion_updates,
).chain());
} }
} }
+70 -5
View File
@@ -1,5 +1,5 @@
use crate::constants::*; use crate::constants::*;
use crate::tile::{FixtureTile, FloorTile, TileState}; use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileState};
use bevy::prelude::*; use bevy::prelude::*;
#[derive(Bundle)] #[derive(Bundle)]
@@ -8,15 +8,16 @@ pub struct FloorTilePrefab {
sprite: Sprite, sprite: Sprite,
tile: FloorTile, tile: FloorTile,
tile_state: TileState, tile_state: TileState,
visibility: Visibility visibility: Visibility,
needs_occluded: NeedsOccluded,
} }
impl FloorTilePrefab { impl FloorTilePrefab {
pub fn dirt(position: Vec3, asset_server: &Res<AssetServer>) -> Self { pub fn grass(position: Vec3, asset_server: &Res<AssetServer>) -> Self {
FloorTilePrefab { FloorTilePrefab {
transform: Transform::from_translation(position).with_scale(Vec3::splat(PIXEL_RATIO)), transform: Transform::from_translation(position).with_scale(Vec3::splat(PIXEL_RATIO)),
sprite: Sprite { sprite: Sprite {
image: asset_server.load("dirt_floor.png"), image: asset_server.load("grass_floor.png"),
..Default::default() ..Default::default()
}, },
tile: FloorTile { tile: FloorTile {
@@ -27,6 +28,30 @@ impl FloorTilePrefab {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
}, },
visibility: Visibility::Hidden, visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
}
}
pub fn dirt(position: Vec3, asset_server: &Res<AssetServer>) -> Self {
FloorTilePrefab {
transform: Transform::from_translation(position).with_scale(Vec3::splat(PIXEL_RATIO)),
sprite: Sprite {
image: asset_server.load("dirt_floor.png"),
..Default::default()
},
tile: FloorTile {
id: 2,
..Default::default()
},
tile_state: TileState {
timer: Timer::from_seconds(1.0, TimerMode::Repeating),
},
visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
} }
} }
@@ -45,6 +70,9 @@ impl FloorTilePrefab {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
}, },
visibility: Visibility::Hidden, visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
} }
} }
@@ -64,6 +92,9 @@ impl FloorTilePrefab {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
}, },
visibility: Visibility::Hidden, visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
} }
} }
@@ -82,8 +113,22 @@ impl FloorTilePrefab {
timer: Timer::from_seconds(1.0, TimerMode::Repeating), timer: Timer::from_seconds(1.0, TimerMode::Repeating),
}, },
visibility: Visibility::Hidden, visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
} }
} }
pub fn spawn(self, commands: &mut Commands) {
commands.spawn((
self.tile,
self.transform,
self.sprite,
self.tile_state,
self.visibility,
self.needs_occluded,
));
}
} }
#[derive(Bundle)] #[derive(Bundle)]
@@ -91,7 +136,8 @@ pub struct FixtureTilePrefab {
transform: Transform, transform: Transform,
sprite: Sprite, sprite: Sprite,
tile: FixtureTile, tile: FixtureTile,
visibility: Visibility visibility: Visibility,
needs_occluded: NeedsOccluded,
} }
impl FixtureTilePrefab { impl FixtureTilePrefab {
@@ -107,6 +153,9 @@ impl FixtureTilePrefab {
..Default::default() ..Default::default()
}, },
visibility: Visibility::Hidden, visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
} }
} }
@@ -122,6 +171,9 @@ impl FixtureTilePrefab {
..Default::default() ..Default::default()
}, },
visibility: Visibility::Hidden, visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
} }
} }
@@ -137,6 +189,19 @@ impl FixtureTilePrefab {
..Default::default() ..Default::default()
}, },
visibility: Visibility::Hidden, visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
} }
} }
pub fn spawn(self, commands: &mut Commands) {
commands.spawn((
self.tile,
self.transform,
self.sprite,
self.visibility,
self.needs_occluded,
));
}
} }