From c061ecc08212f40fd155d1aaf46bb24fa069c2a7 Mon Sep 17 00:00:00 2001 From: popertots Date: Sun, 4 May 2025 01:08:58 +0100 Subject: [PATCH] fix terrain z scrolling --- src/tile.rs | 30 ++++++++++++++++------- src/tilemap.rs | 64 ++++++++++++++++++++++++-------------------------- src/tiles.rs | 43 +++++++++++++++++++-------------- 3 files changed, 79 insertions(+), 58 deletions(-) diff --git a/src/tile.rs b/src/tile.rs index 9ef0a24..c72403f 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -1,4 +1,6 @@ -use crate::{game, tiles}; +use std::time::Instant; + +use crate::{game, tilemap, tiles}; use bevy::prelude::*; use bevy_platform::collections::hash_map::HashMap; @@ -56,13 +58,13 @@ pub fn camera_z_movement( camera_moved.0 = false; if keyboard_input.just_pressed(KeyCode::ShiftLeft) { z_index.0 -= 1.0; - z_index.0 = z_index.0.clamp(-149.0, 5.0); + z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE); camera_moved.0 = true; println!("z_index = {}", z_index.0); } if keyboard_input.just_pressed(KeyCode::ShiftRight) { z_index.0 += 1.0; - z_index.0 = z_index.0.clamp(-149.0, 5.0); + z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE); camera_moved.0 = true; println!("z_index = {}", z_index.0); } @@ -74,13 +76,25 @@ pub struct TileMap { pub fixture_tiles: HashMap, } -//TODO, redo +// This system updates sprite visibility based on current z-index pub fn update_tile_visibility( - // z_index: ResMut, - mut cwss: ResMut, + z_index: Res, + mut query: Query<(&tiles::TerrainSprite, &mut Visibility)>, ) { - cwss.state = tiles::WorldSpriteState::WaitingForRender; - // let z_index = (z_index.0 as i32 + 150) as usize; + let now = Instant::now(); + + let current_z = z_index.0 as isize; + + // Update visibility for all terrain sprites + for (terrain_sprite, mut visibility) in query.iter_mut() { + *visibility = + if terrain_sprite.z_index == ((current_z + tilemap::Z_BELOW as isize) as usize) { + Visibility::Visible + } else { + Visibility::Hidden + }; + } + println!("Visibility update: {:.2?}", now.elapsed()); } #[derive(Component)] diff --git a/src/tilemap.rs b/src/tilemap.rs index b17f7b8..947a279 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -1,12 +1,16 @@ use crate::constants::{ITILE_SIZE, TILE_SIZE}; -use crate::tile::{self, FixtureTile, FloorTile, NeedsOccluded, TileMap}; -use crate::tiles::{self, FixtureTilePrefab, FloorTilePrefab}; +use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileMap}; +use crate::tiles::{CurrentWorldSpriteState, FixtureTilePrefab, FloorTilePrefab, WorldSpriteState}; use bevy::prelude::*; use bevy_platform::collections::hash_map::HashMap; use noise::{NoiseFn, Perlin}; pub const CHUNK_SIZE: i32 = 8; +pub const Z_BELOW: f32 = 30.0; +pub const Z_ABOVE: f32 = 5.0; +pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; + #[derive(Resource)] pub struct ChunkMap { pub loaded_chunks: HashMap, @@ -38,7 +42,7 @@ pub fn handle_tile_occlusion_updates( Query<(&mut FixtureTile, &Transform, &mut NeedsOccluded)>, Query<(Entity, &mut NeedsOccluded)>, )>, - mut cwss: ResMut, + mut cwss: ResMut, ) { query_set .p0() @@ -69,13 +73,13 @@ pub fn handle_tile_occlusion_updates( commands.entity(entity).remove::(); } } - cwss.state = tiles::WorldSpriteState::WaitingForRender; + cwss.state = WorldSpriteState::WaitingForRender; } pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { let mut visible_range = [0u32; 8]; - for mut camera_z in -15..=5 { + for mut camera_z in -Z_BELOW as i32..=Z_ABOVE as i32 { camera_z *= ITILE_SIZE; let mut is_visible = false; @@ -85,7 +89,7 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { let mut is_occluded = false; - 'vertical_check: for z_offset in 1..10 { + 'vertical_check: for z_offset in 1..15 { let above_pos = IVec3::new(pos.x, pos.y, pos.z + (z_offset * ITILE_SIZE)); if above_pos.z <= camera_z { if let Some(&(_, opaque, _, _, _)) = tilemap.floor_tiles.get(&above_pos) { @@ -94,16 +98,6 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { break 'vertical_check; } } - // if z_offset >= 4 { - // is_occluded = true; - // break 'vertical_check; - // } - // if let Some(&(_, solid, _)) = tilemap.fixtures.get(&above_pos) { - // if solid { - // is_occluded = true; - // break 'vertical_check; - // } - // } } else { break 'vertical_check; } @@ -127,19 +121,13 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { break 'neighbor_check; } } - // if let Some(&(id, _, _)) = tilemap.fixtures.get(&neighbor_pos) { - // if id == 0 { - // is_visible = true; - // break 'neighbor_check; - // } - // } } } } } if is_visible { - let z2 = ((camera_z / ITILE_SIZE) + 150) as usize; + let z2 = ((camera_z / ITILE_SIZE) + Z_BELOW as i32) as usize; visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32); } } @@ -158,7 +146,6 @@ pub fn generate_surface_noise(x: i32, y: i32) -> f32 { amplitude *= 0.6; frequency *= 1.8; } - (noise_value * 2.5) as f32 } @@ -168,7 +155,7 @@ fn generate_chunks_from_algo( mut chunk_map: ResMut, mut tilemap: ResMut, ) { - let noise = Perlin::new(0); + let cave_noise = Perlin::new(0); for event in events.read() { println!("Loading chunk {}", event.chunk_position); @@ -197,7 +184,7 @@ fn generate_chunks_from_algo( ); // Spawn tiles and add them to tilemap - for z in -10..=5 { + for z in -Z_BELOW as isize..=Z_ABOVE as isize { let position = Vec3::new( (world_x as f32 * TILE_SIZE).round(), (world_y as f32 * TILE_SIZE).round(), @@ -206,7 +193,7 @@ fn generate_chunks_from_algo( let pos_ivec = position.as_ivec3(); if z < -8 { - let noise_value = noise.get([ + let noise_value = cave_noise.get([ world_x as f64 * 0.05, world_y as f64 * 0.05, z as f64 * 0.05, @@ -295,6 +282,22 @@ fn setup_initial_chunks(mut event_writer: EventWriter) { } } +pub fn index_z_to_absolute_z(z: f32) -> f32 { + z + Z_BELOW +} + +pub fn world_z_to_absolute_z(z: f32) -> f32 { + z + Z_BELOW * ITILE_SIZE as f32 +} + +pub fn absolute_z_to_index_z(z: f32) -> f32 { + z - Z_BELOW +} + +pub fn absolute_z_to_world_z(z: f32) -> f32 { + z - Z_BELOW * ITILE_SIZE as f32 +} + pub struct TilemapPlugin; impl Plugin for TilemapPlugin { @@ -304,12 +307,7 @@ impl Plugin for TilemapPlugin { .add_systems(Startup, (setup_chunk_system, setup_initial_chunks)) .add_systems( PostStartup, - ( - generate_chunks_from_algo, - handle_tile_occlusion_updates, - tile::update_tile_visibility, - ) - .chain(), + (generate_chunks_from_algo, handle_tile_occlusion_updates).chain(), ) .add_systems(FixedUpdate, (generate_chunks_from_algo).chain()); } diff --git a/src/tiles.rs b/src/tiles.rs index 1e7c374..df0607f 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -1,5 +1,5 @@ use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileState}; -use crate::{constants::*, game}; +use crate::{constants::*, game, tilemap}; use bevy::prelude::*; use bevy_platform::collections::hash_map::HashMap; @@ -92,34 +92,34 @@ pub struct CurrentWorldSpriteState { use std::time::Instant; #[derive(Component)] -pub struct TerrainSprite; +pub struct TerrainSprite { + pub z_index: usize, // Store the z-index this sprite belongs to (as world_Z) +} -// TODO: re-rendering on z-index change is inefficient, replace with shifting pool system for z-index change +// This system generates sprites for all z-indices and stores references to them pub fn build_world_sprites( - mut query: Query<(&FloorTile, &Transform)>, + query: Query<(&FloorTile, &Transform)>, mut commands: Commands, mut cwss: ResMut, textures: Res, texture_ids: Res, - z_index: ResMut, query_1: Query>, ) { if cwss.state != WorldSpriteState::WaitingForRender { return; } - // despawn all query_1 entities + let now = Instant::now(); + cwss.state = WorldSpriteState::InProgress; + + // despawn all existing terrain sprites for entity in query_1.iter() { commands.entity(entity).despawn(); } - let now = Instant::now(); - if cwss.state == WorldSpriteState::WaitingForRender { - cwss.state = WorldSpriteState::InProgress; - let z_index = (z_index.0 as i32 + 150) as usize; - println!("z_index = {}", z_index); - - for (floortile, transform) in query.iter_mut() { + // Generate sprites for all z-indices (0 to 150) + for z_index in 0..=tilemap::Z_TOTAL as usize { + for (floortile, transform) in query.iter() { let is_visible = (floortile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0; if is_visible { @@ -130,14 +130,23 @@ pub fn build_world_sprites( image: texture.clone(), ..Default::default() }; - commands.spawn((sprite, *transform, TerrainSprite)); + + // Spawn with hidden visibility initially - update_tile_visibility will handle showing/hiding + commands.spawn(( + sprite, + *transform, + TerrainSprite { z_index }, + Visibility::Hidden, + )); } } - cwss.state = WorldSpriteState::RenderReady; } - println!("{:?}", cwss.state); + + cwss.state = WorldSpriteState::RenderReady; + let elapsed = now.elapsed(); - println!("Elapsed: {:.2?}", elapsed); + + println!("World sprites built. Elapsed: {:.2?}", elapsed); } #[derive(Bundle)]