working path finding, non working targeting

This commit is contained in:
2025-01-24 17:20:45 +00:00
parent ca3fcc00ff
commit 7143a9dded
3 changed files with 100 additions and 103 deletions
+89 -92
View File
@@ -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<AssetServer>, 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;
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<Vec3> {
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<Vec3> {
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[&current.position] + movement_cost;
let new_g = g_scores.get(&current).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<AssetServer>) {
// 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));
+1 -1
View File
@@ -53,7 +53,7 @@ fn main() {
tile::camera_z_movement,
tile::update_tile_visibility
.run_if(|camera_moved: Res<tile::CameraMoved>| camera_moved.0),
// log_fps_system,
log_fps_system,
),
)
.run();
+8 -8
View File
@@ -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<LoadChunkEvent>) {
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),
});