From 7143a9dded11b7218634a2a1ee14648c2a3351d3 Mon Sep 17 00:00:00 2001 From: popertots Date: Fri, 24 Jan 2025 17:20:45 +0000 Subject: [PATCH] working path finding, non working targeting --- src/citizen.rs | 185 ++++++++++++++++++++++++------------------------- src/main.rs | 2 +- src/tilemap.rs | 16 ++--- 3 files changed, 100 insertions(+), 103 deletions(-) diff --git a/src/citizen.rs b/src/citizen.rs index df01a6b..be9638c 100644 --- a/src/citizen.rs +++ b/src/citizen.rs @@ -1,14 +1,13 @@ use crate::{ constants::*, - tile, - tilemap::{generate_surface_noise, ChunkMap, CHUNK_SIZE}, + tilemap::{ChunkMap, CHUNK_SIZE}, }; use bevy::{math::ivec3, prelude::*}; -use noise::Negate; +use noise::Vector3; #[derive(Bundle)] pub struct Citizen { - walkness: Ambulatory, + ambulatory: Ambulatory, sprite: Sprite, transform: Transform, } @@ -16,7 +15,7 @@ pub struct Citizen { impl Citizen { pub fn new(asset_server: &Res, position: Vec3) -> Self { Citizen { - walkness: Ambulatory { + ambulatory: Ambulatory { speed: TILE_SIZE, target: None, current_path: None, @@ -72,7 +71,7 @@ impl Plugin for PathfindingPlugin { fn build(&self, app: &mut App) { app.add_systems( FixedUpdate, - (update_citizen_wandering_targets, citizen_movement), + (update_citizen_wandering_targets, citizen_movement).chain(), ); } } @@ -84,7 +83,7 @@ pub fn update_citizen_wandering_targets( ) { let mut rng = rand::thread_rng(); - for (mut ambulatory, transform) in query.iter_mut() { + for (mut ambulatory, _) in query.iter_mut() { if ambulatory.target.is_none() || (ambulatory.current_path.is_some() && ambulatory.path_index >= ambulatory.current_path.as_ref().unwrap().len()) @@ -114,7 +113,7 @@ pub fn update_citizen_wandering_targets( ambulatory.target = Some(Vec3::new( target_x as f32 * TILE_SIZE, target_y as f32 * TILE_SIZE, - z as f32 * TILE_SIZE + 1.0, + z as f32 * TILE_SIZE + TILE_SIZE + 1.0, )); ambulatory.current_path = None; ambulatory.path_index = 0; @@ -142,22 +141,25 @@ pub fn citizen_movement( current_pos.z - TILE_SIZE - 0.1, ); - // Must have solid ground below - if let Some(floor_tile) = tilemap.floor_tiles.get(&below_pos.as_ivec3()) { - if !floor_tile.1 { - transform.translation.z -= TILE_SIZE; - continue; - } + if !is_standable_tile(&tilemap, current_pos.as_ivec3()) { + // println!("drop"); + transform.translation = below_pos + Vec3::new(0.0, 0.0, 0.1); + // println!("transform.translation: {:?}", transform.translation); + continue; + } + let mut rng = rand::thread_rng(); + if rng.gen_range(0..8) > 0 { + continue; } if let Some(target) = ambulatory.target { // Calculate path if needed if ambulatory.current_path.is_none() { - println!( - "from {:?} to {:?}", - transform.translation.as_ivec3(), - target.as_ivec3() - ivec3(0, 0, 1) - ); + // println!( + // "from {:?} to {:?}", + // transform.translation.as_ivec3(), + // target.as_ivec3() - ivec3(0, 0, 1) + // ); ambulatory.current_path = Some(calculate_path( &tilemap, transform.translation.as_ivec3(), @@ -170,22 +172,19 @@ pub fn citizen_movement( if let Some(path) = &ambulatory.current_path { if ambulatory.path_index < path.len() { let next_point = path[ambulatory.path_index]; - let next_world_pos = Vec3::new( - next_point.x + TILE_SIZE / 2.0, - next_point.y + TILE_SIZE / 2.0, - next_point.z + TILE_SIZE / 2.0, - ); - let direction = (next_world_pos - transform.translation).normalize(); - transform.translation += direction * ambulatory.speed; + let direction = (next_point - transform.translation).normalize(); + transform.translation = next_point; // Update sprite direction (only for x movement) - if direction.x != 0.0 { - transform.scale.x = transform.scale.x.abs() * direction.x.signum(); + if direction.x > 0.0 { + transform.scale.x = PIXEL_RATIO; + } else if direction.x < 0.0 { + transform.scale.x = -PIXEL_RATIO; } // Check if we've reached the next point - if transform.translation.distance(next_world_pos) < ambulatory.speed { + if transform.translation.distance(next_point) < ambulatory.speed { ambulatory.path_index += 1; } } @@ -194,7 +193,7 @@ pub fn citizen_movement( } } -fn is_valid_move(tilemap: &TileMap, pos: IVec3) -> bool { +fn is_standable_tile(tilemap: &TileMap, pos: IVec3) -> bool { // Check if the tile at current position is not blocking if let Some(current_tile) = tilemap.floor_tiles.get(&pos) { if current_tile.0 != 0 { @@ -211,6 +210,17 @@ fn is_valid_move(tilemap: &TileMap, pos: IVec3) -> bool { } fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { + if !is_standable_tile(tilemap, start) { + println!("Start pos invalid: {}", start); + println!("Bugger (1)"); + exit(0); + } + if !is_standable_tile(tilemap, goal) { + println!("Goal pos invalid: {}", goal); + println!("Bugger (2)"); + exit(0); + } + let mut open_set = BinaryHeap::new(); let mut came_from = HashMap::new(); let mut g_scores = HashMap::new(); @@ -218,103 +228,90 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { let start_node = PathNode { position: start, - f_score: 0, + f_score: manhattan_distance_3d(start, goal), g_score: 0, }; - open_set.push(start_node.clone()); + open_set.push(start_node); g_scores.insert(start, 0); - // Define allowed movements: orthogonal + diagonal on same level, and up/down let allowed_moves = vec![ - // Cardinal directions (same level) - IVec3::new(ITILE_SIZE, 0, 0), + // Orthogonal moves IVec3::new(-ITILE_SIZE, 0, 0), - IVec3::new(0, ITILE_SIZE, 0), + IVec3::new(ITILE_SIZE, 0, 0), IVec3::new(0, -ITILE_SIZE, 0), - // Diagonal directions (same level) - IVec3::new(ITILE_SIZE, ITILE_SIZE, 0), - IVec3::new(ITILE_SIZE, -ITILE_SIZE, 0), - IVec3::new(-ITILE_SIZE, ITILE_SIZE, 0), + IVec3::new(0, ITILE_SIZE, 0), + // Diagonal moves IVec3::new(-ITILE_SIZE, -ITILE_SIZE, 0), + IVec3::new(-ITILE_SIZE, ITILE_SIZE, 0), + IVec3::new(ITILE_SIZE, -ITILE_SIZE, 0), + IVec3::new(ITILE_SIZE, ITILE_SIZE, 0), + // Diagonal with vertical moves (left/negative preference) + IVec3::new(-ITILE_SIZE, 0, ITILE_SIZE), + IVec3::new(-ITILE_SIZE, 0, -ITILE_SIZE), + IVec3::new(ITILE_SIZE, 0, ITILE_SIZE), + IVec3::new(ITILE_SIZE, 0, -ITILE_SIZE), + IVec3::new(0, -ITILE_SIZE, ITILE_SIZE), + IVec3::new(0, -ITILE_SIZE, -ITILE_SIZE), + IVec3::new(0, ITILE_SIZE, ITILE_SIZE), + IVec3::new(0, ITILE_SIZE, -ITILE_SIZE), + // Full 3D diagonal moves + IVec3::new(-ITILE_SIZE, -ITILE_SIZE, ITILE_SIZE), + IVec3::new(-ITILE_SIZE, -ITILE_SIZE, -ITILE_SIZE), + IVec3::new(-ITILE_SIZE, ITILE_SIZE, ITILE_SIZE), + IVec3::new(-ITILE_SIZE, ITILE_SIZE, -ITILE_SIZE), + IVec3::new(ITILE_SIZE, -ITILE_SIZE, ITILE_SIZE), + IVec3::new(ITILE_SIZE, -ITILE_SIZE, -ITILE_SIZE), + IVec3::new(ITILE_SIZE, ITILE_SIZE, ITILE_SIZE), + IVec3::new(ITILE_SIZE, ITILE_SIZE, -ITILE_SIZE), ]; - while let Some(current) = open_set.pop() { - if current.position == goal { + while let Some(current_node) = open_set.pop() { + let current = current_node.position; + + if current == goal { println!("path found"); - return reconstruct_path(came_from, current.position); + return reconstruct_path(came_from, current); } - closed_set.insert(current.position); + closed_set.insert(current); - for move_dir in &allowed_moves { - // Try same level - let mut neighbor_pos = current.position + *move_dir; + for &move_dir in &allowed_moves { + let neighbor_pos = current + move_dir; - // Try one up if same level is invalid - let up_pos = neighbor_pos + IVec3::new(0, 0, ITILE_SIZE); - if !is_valid_move(tilemap, neighbor_pos) && is_valid_move(tilemap, up_pos) { - neighbor_pos = up_pos; - } - - // Try one down if same level is invalid - let down_pos = neighbor_pos - IVec3::new(0, 0, ITILE_SIZE); - if !is_valid_move(tilemap, neighbor_pos) && is_valid_move(tilemap, down_pos) { - neighbor_pos = down_pos; - } - - if !is_valid_move(tilemap, neighbor_pos) || closed_set.contains(&neighbor_pos) { + if !is_standable_tile(tilemap, neighbor_pos) || closed_set.contains(&neighbor_pos) { continue; } - - let current_level = ITILE_SIZE; - let upper = 2*ITILE_SIZE; - let lower = -ITILE_SIZE; - - // Calculate movement cost let movement_cost = match ( - move_dir.x.abs() + move_dir.y.abs(), - neighbor_pos.z - current.position.z, + move_dir.x.abs() / ITILE_SIZE + move_dir.y.abs() / ITILE_SIZE, + move_dir.z.abs() / ITILE_SIZE, ) { - (current_level, 0) => 10, // Orthogonal movement - (upper, 0) => 14, // Diagonal movement - (_, current_level) => 20, // Moving up one level - (_, lower) => 15, // Moving down one level + (1, 0) => 10, // Orthogonal movement + (2, 0) => 10, // Diagonal movement + (_, 1) => 14, // Moving up or down _ => continue, // Invalid movement }; - let new_g = g_scores[¤t.position] + movement_cost; + let new_g = g_scores.get(¤t).unwrap_or(&i32::MAX) + movement_cost; - if !g_scores.contains_key(&neighbor_pos) || new_g < g_scores[&neighbor_pos] { + if new_g < *g_scores.get(&neighbor_pos).unwrap_or(&i32::MAX) { + came_from.insert(neighbor_pos, current); g_scores.insert(neighbor_pos, new_g); let h = manhattan_distance_3d(neighbor_pos, goal); let f = new_g + h; - open_set.push(PathNode { + let neighbor_node = PathNode { position: neighbor_pos, f_score: f, g_score: new_g, - }); - came_from.insert(neighbor_pos, current.position); + }; + open_set.push(neighbor_node); } } } - println!("No path found"); - // If no path found, return direct line - vec![ - Vec3::new(start.x as f32, start.y as f32, start.z as f32), - Vec3::new(goal.x as f32, goal.y as f32, goal.z as f32), - ] -} - -fn is_walkable(tilemap: &TileMap, position: IVec3) -> bool { - if let Some(floor_tile) = tilemap.floor_tiles.get(&position) { - floor_tile.2 // walkable - } else { - false - } + Vec::new() } fn manhattan_distance_3d(a: IVec3, b: IVec3) -> i32 { @@ -346,9 +343,9 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res) { // Spawn a handful of citizens for _ in 0..1 { - let x: f32 = rng.gen_range(-15.0..10.0); - let y: f32 = rng.gen_range(-10.0..10.0); - let mut position = Vec3::new(x.round(), y.round(), 5.0) * TILE_SIZE; + let x: f32 = rng.gen_range(-8.0..8.0); + let y: f32 = rng.gen_range(-8.0..8.0); + let mut position = Vec3::new(x.round(), y.round(), 30.0) * TILE_SIZE; position.z += 0.1; commands.spawn(Citizen::new(&asset_server, position)); diff --git a/src/main.rs b/src/main.rs index 30c3130..e54ffa6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,7 +53,7 @@ fn main() { tile::camera_z_movement, tile::update_tile_visibility .run_if(|camera_moved: Res| camera_moved.0), - // log_fps_system, + log_fps_system, ), ) .run(); diff --git a/src/tilemap.rs b/src/tilemap.rs index e51aea2..309c0b4 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -84,7 +84,7 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { let mut is_occluded = false; - 'vertical_check: for z_offset in 1..5 { + 'vertical_check: for z_offset in 1..10 { 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) { @@ -93,10 +93,10 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { break 'vertical_check; } } - if z_offset >= 4 { - is_occluded = true; - 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; @@ -198,7 +198,7 @@ fn handle_chunk_loading( ); // Spawn tiles and add them to tilemap - for z in -15..=5 { + for z in -10..=5 { let position = Vec3::new( (world_x as f32 * TILE_SIZE).round(), (world_y as f32 * TILE_SIZE).round(), @@ -287,8 +287,8 @@ fn handle_chunk_loading( fn setup_initial_chunks(mut event_writer: EventWriter) { println!("setup_initial_chunks"); - for x in -8..=8 { - for y in -8..=8 { + for x in -3..=3 { + for y in -2..=2 { event_writer.send(LoadChunkEvent { chunk_position: IVec2::new(x, y), });