From 9df3f7ecaf1e875c937e3591ed93ebf263f25b51 Mon Sep 17 00:00:00 2001 From: popertots Date: Sat, 3 May 2025 20:27:44 +0100 Subject: [PATCH] manhattan to octile --- src/citizen.rs | 74 ++++++++++++++++++++++++++++++++++++++------------ src/tile.rs | 3 -- src/tilemap.rs | 2 +- src/tiles.rs | 2 +- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/citizen.rs b/src/citizen.rs index 65b9fbb..1a18eb6 100644 --- a/src/citizen.rs +++ b/src/citizen.rs @@ -144,9 +144,7 @@ pub fn citizen_movement( ); 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; } @@ -185,6 +183,9 @@ pub fn citizen_movement( if transform.translation.distance(next_point) < ambulatory.speed { ambulatory.path_index += 1; } + } else { + ambulatory.current_path = None; + ambulatory.target = None; } } } @@ -223,14 +224,16 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { let mut came_from = HashMap::new(); let mut g_scores = HashMap::new(); let mut closed_set = HashSet::new(); + let mut in_open_set = HashSet::new(); let start_node = PathNode { position: start, - f_score: manhattan_distance_3d(start, goal), + f_score: octile_distance_3d(start, goal), g_score: 0, }; open_set.push(start_node); + in_open_set.insert(start); g_scores.insert(start, 0); let allowed_moves = vec![ @@ -267,6 +270,8 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { while let Some(current_node) = open_set.pop() { let current = current_node.position; + in_open_set.remove(¤t); + if current == goal { // println!("path found"); return reconstruct_path(came_from, current); @@ -282,13 +287,14 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { } let movement_cost = match ( - move_dir.x.abs() / ITILE_SIZE + move_dir.y.abs() / ITILE_SIZE, + move_dir.x.abs() / ITILE_SIZE, + move_dir.y.abs() / ITILE_SIZE, move_dir.z.abs() / ITILE_SIZE, ) { - (1, 0) => 10, // Orthogonal movement - (2, 0) => 11, // Diagonal movement - (_, 1) => 14, // Moving up or down - _ => continue, // Invalid movement + (1, 0, 0) | (0, 1, 0) | (0, 0, 1) => 10, // Orthogonal movement (1 axis) + (1, 1, 0) | (1, 0, 1) | (0, 1, 1) => 14, // Diagonal movement (2 axes) + (1, 1, 1) => 17, // Full 3D diagonal movement (3 axes) + _ => continue, // Invalid movement }; let new_g = g_scores.get(¤t).unwrap_or(&i32::MAX) + movement_cost; @@ -296,15 +302,29 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { 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 h = octile_distance_3d(neighbor_pos, goal); let f = new_g + h; - let neighbor_node = PathNode { - position: neighbor_pos, - f_score: f, - g_score: new_g, - }; - open_set.push(neighbor_node); + // Only add to open set if not already there + if !in_open_set.contains(&neighbor_pos) { + let neighbor_node = PathNode { + position: neighbor_pos, + f_score: f, + g_score: new_g, + }; + open_set.push(neighbor_node); + in_open_set.insert(neighbor_pos); + } else { + // For nodes already in the open set, we need to update their priority + // This is tricky with a binary heap - one approach is to add a new entry + // The old one will still be there but will be ignored when we check the closed set + let neighbor_node = PathNode { + position: neighbor_pos, + f_score: f, + g_score: new_g, + }; + open_set.push(neighbor_node); + } } } } @@ -312,8 +332,26 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { Vec::new() } -fn manhattan_distance_3d(a: IVec3, b: IVec3) -> i32 { - (a.x - b.x).abs() + (a.y - b.y).abs() + (a.z - b.z).abs() +fn octile_distance_3d(a: IVec3, b: IVec3) -> i32 { + let dx = (a.x - b.x).abs(); + let dy = (a.y - b.y).abs(); + let dz = (a.z - b.z).abs(); + + // Costs for movement along 1, 2, or 3 axes + let cost1 = 10; // Orthogonal + let cost2 = 14; // 2D Diagonal + let cost3 = 17; // 3D Diagonal + + // The heuristic calculation sorts the dimensions implicitly + // It calculates the minimum number of diagonal steps needed + let mut diffs = [dx, dy, dz]; + diffs.sort_unstable(); // Sorts ascending: [dmin, dmid, dmax] + + let dmin = diffs[0]; + let dmid = diffs[1]; + let dmax = diffs[2]; + + cost3 * dmin + cost2 * (dmid - dmin) + cost1 * (dmax - dmid) // Yikes.. } fn reconstruct_path(came_from: HashMap, mut current: IVec3) -> Vec { @@ -340,7 +378,7 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res) { let mut rng = rand::rng(); // Spawn a handful of citizens - for _ in 0..5 { + for _ in 0..50 { let x: f32 = rng.random_range(-8.0..8.0); let y: f32 = rng.random_range(-8.0..8.0); let mut position = Vec3::new(x.round(), y.round(), 30.0) * TILE_SIZE; diff --git a/src/tile.rs b/src/tile.rs index 5d38111..9ef0a24 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -79,10 +79,7 @@ pub fn update_tile_visibility( // z_index: ResMut, mut cwss: ResMut, ) { - println!("update_tile_visibility, cwss.state = {:?}", cwss.state); cwss.state = tiles::WorldSpriteState::WaitingForRender; - println!("update_tile_visibility, cwss.state = {:?}", cwss.state); - println!(); // let z_index = (z_index.0 as i32 + 150) as usize; } diff --git a/src/tilemap.rs b/src/tilemap.rs index 818eeda..b17f7b8 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -288,7 +288,7 @@ fn setup_initial_chunks(mut event_writer: EventWriter) { println!("setup_initial_chunks"); for x in -7..=7 { for y in -7..=7 { - event_writer.send(LoadChunkEvent { + event_writer.write(LoadChunkEvent { chunk_position: IVec2::new(x, y), }); } diff --git a/src/tiles.rs b/src/tiles.rs index 61fd128..1e7c374 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -1,4 +1,3 @@ - use crate::tile::{FixtureTile, FloorTile, NeedsOccluded, TileState}; use crate::{constants::*, game}; use bevy::prelude::*; @@ -89,6 +88,7 @@ pub enum WorldSpriteState { pub struct CurrentWorldSpriteState { pub state: WorldSpriteState, } + use std::time::Instant; #[derive(Component)]