diff --git a/engine/world_generation/src/hex_utils.rs b/engine/world_generation/src/hex_utils.rs index 032ffb4..c7fbb75 100644 --- a/engine/world_generation/src/hex_utils.rs +++ b/engine/world_generation/src/hex_utils.rs @@ -56,7 +56,7 @@ pub fn tile_to_world_distance(dist: u32) -> f32 { return dist as f32 * (2. * INNER_RADIUS); } -pub fn get_tile_count(radius: usize) -> usize { +pub fn get_tile_count_in_range(radius: usize) -> usize { return 1 + 3 * (radius + 1) * radius; } @@ -240,7 +240,7 @@ impl HexCoord { pub fn hex_select(&self, radius: usize, include_center: bool) -> Vec { assert!(radius != 0, "Radius cannot be zero"); - let mut result = Vec::with_capacity(get_tile_count(radius)); + let mut result = Vec::with_capacity(get_tile_count_in_range(radius)); if include_center { result.push(*self); @@ -267,7 +267,7 @@ impl HexCoord { width: usize, ) -> Vec { assert!(radius != 0, "Radius cannot be zero"); - let mut result = Vec::with_capacity(get_tile_count(radius)); + let mut result = Vec::with_capacity(get_tile_count_in_range(radius)); if include_center { if self.is_in_bounds(height, width) { @@ -296,10 +296,10 @@ impl HexCoord { let mut p = self.scale(4, radius); - if radius == 1 { - result.push(*self); - return result; - } + // if radius == 1 { + // result.push(*self); + // return result; + // } for i in 0..6 { for _j in 0..radius { diff --git a/engine/world_generation/src/map/map.rs b/engine/world_generation/src/map/map.rs index 69987f7..9ac52d3 100644 --- a/engine/world_generation/src/map/map.rs +++ b/engine/world_generation/src/map/map.rs @@ -180,9 +180,9 @@ impl Map { assert!(radius != 0, "Radius cannot be zero"); let mut result = if include_center { - Vec::with_capacity(get_tile_count(radius) + 1) + Vec::with_capacity(get_tile_count_in_range(radius) + 1) } else { - Vec::with_capacity(get_tile_count(radius)) + Vec::with_capacity(get_tile_count_in_range(radius)) }; if include_center { let h = self.sample_height(¢er); @@ -218,9 +218,9 @@ impl Map { assert!(radius != 0, "Radius cannot be zero"); let mut result = if include_center { - Vec::with_capacity(get_tile_count(radius) + 1) + Vec::with_capacity(get_tile_count_in_range(radius) + 1) } else { - Vec::with_capacity(get_tile_count(radius)) + Vec::with_capacity(get_tile_count_in_range(radius)) }; if include_center { let h = self.sample_height_mut(¢er); diff --git a/game/main/src/map_rendering/map_init.rs b/game/main/src/map_rendering/map_init.rs index 213eda3..4f5b7dd 100644 --- a/game/main/src/map_rendering/map_init.rs +++ b/game/main/src/map_rendering/map_init.rs @@ -34,11 +34,10 @@ use crate::{ }, utlis::{ chunk_utils::{paint_map, prepare_chunk_mesh_with_collider}, - render_distance_system::RenderDistanceVisibility, }, }; -use super::{chunk_rebuild::ChunkRebuildPlugin, terraforming_test::TerraFormingTestPlugin}; +use super::{chunk_rebuild::ChunkRebuildPlugin, render_distance_system::RenderDistanceVisibility, terraforming_test::TerraFormingTestPlugin}; pub struct MapInitPlugin; diff --git a/game/main/src/map_rendering/mod.rs b/game/main/src/map_rendering/mod.rs index 44fe72b..96454ba 100644 --- a/game/main/src/map_rendering/mod.rs +++ b/game/main/src/map_rendering/mod.rs @@ -2,3 +2,4 @@ pub mod chunk_rebuild; pub mod map_init; pub mod prelude; pub mod terraforming_test; +pub mod render_distance_system; diff --git a/game/main/src/utlis/render_distance_system.rs b/game/main/src/map_rendering/render_distance_system.rs similarity index 94% rename from game/main/src/utlis/render_distance_system.rs rename to game/main/src/map_rendering/render_distance_system.rs index 848e32d..e7aaa80 100644 --- a/game/main/src/utlis/render_distance_system.rs +++ b/game/main/src/map_rendering/render_distance_system.rs @@ -9,9 +9,6 @@ impl Plugin for RenderDistancePlugin { app.register_type::(); app.add_systems(PostUpdate, render_distance_system) .insert_resource(RenderDistanceSettings::default()); - - #[cfg(debug_assertions)] - app.insert_resource(RenderDistanceSettings::new(f32::MAX)); } } diff --git a/game/main/src/phos.rs b/game/main/src/phos.rs index fbbc7eb..b9b66ac 100644 --- a/game/main/src/phos.rs +++ b/game/main/src/phos.rs @@ -1,7 +1,7 @@ use crate::camera_system::components::PhosCamera; use crate::map_rendering::map_init::MapInitPlugin; +use crate::map_rendering::render_distance_system::RenderDistancePlugin; use crate::utlis::editor_plugin::EditorPlugin; -use crate::utlis::render_distance_system::RenderDistancePlugin; use crate::utlis::tile_selection_plugin::TileSelectionPlugin; use crate::{camera_system::camera_plugin::PhosCameraPlugin, utlis::debug_plugin::DebugPlugin}; use bevy::{ diff --git a/game/main/src/utlis/debug_plugin.rs b/game/main/src/utlis/debug_plugin.rs index 2ae5a24..8c43248 100644 --- a/game/main/src/utlis/debug_plugin.rs +++ b/game/main/src/utlis/debug_plugin.rs @@ -1,16 +1,7 @@ -use bevy::{prelude::*, window::PrimaryWindow}; -use bevy_inspector_egui::bevy_egui::{systems::InputEvents, EguiContexts}; -use bevy_inspector_egui::egui; -use bevy_rapier3d::prelude::*; +use bevy::prelude::*; use shared::resources::TileUnderCursor; use shared::states::GameplayState; -use shared::tags::MainCamera; -use world_generation::{ - consts::HEX_CORNERS, - hex_utils::{HexCoord, INNER_RADIUS}, - prelude::Map, - states::GeneratorState, -}; +use world_generation::{consts::HEX_CORNERS, prelude::Map, states::GeneratorState}; pub struct DebugPlugin; @@ -76,24 +67,10 @@ fn show_tile_heights(map: Res, mut gizmos: Gizmos, shape: Res, tile_ Quat::IDENTITY, Color::WHITE, ); - let nbors = map.get_neighbors(&contact.tile); - let contact_tile_pos = contact.tile.to_world(map.sample_height(&contact.tile)); - - // for i in 0..6 { - // if let Some(s) = nbors[i] { - // let coord = contact.tile.get_neighbor(i); - // let p = coord.to_world(s); - // gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), Color::WHITE); - // } - - // let p = HEX_CORNERS[i] + contact_tile_pos; - // gizmos.arrow(p, p + Vec3::Y * (i as f32 + 1.0), LinearRgba::rgb(1.0, 0.0, 0.5)); - // } gizmos.line(contact.point, contact.point + Vec3::X, LinearRgba::RED); gizmos.line(contact.point, contact.point + Vec3::Y, LinearRgba::GREEN); gizmos.line(contact.point, contact.point + Vec3::Z, LinearRgba::BLUE); - //gizmos.sphere(contact_point, Quat::IDENTITY, 0.1, LinearRgba::rgb(1., 0., 0.5)); } } diff --git a/game/main/src/utlis/mod.rs b/game/main/src/utlis/mod.rs index 86dcd04..8fc95da 100644 --- a/game/main/src/utlis/mod.rs +++ b/game/main/src/utlis/mod.rs @@ -1,5 +1,4 @@ pub mod chunk_utils; -pub mod render_distance_system; pub mod debug_plugin; pub mod editor_plugin; pub mod tile_selection_plugin; diff --git a/game/units/src/components.rs b/game/units/src/components.rs index 2296d5e..2342420 100644 --- a/game/units/src/components.rs +++ b/game/units/src/components.rs @@ -26,4 +26,4 @@ pub struct Target(pub HexCoord); pub struct Path(pub Vec, pub usize); #[derive(Component, Debug)] -pub struct PathTask(pub Task); +pub struct PathTask(pub Task>); diff --git a/game/units/src/nav_data.rs b/game/units/src/nav_data.rs index 071a413..895249d 100644 --- a/game/units/src/nav_data.rs +++ b/game/units/src/nav_data.rs @@ -1,10 +1,8 @@ +use bevy::prelude::Resource; use ordered_float::OrderedFloat; -use world_generation::{ - hex_utils::HexCoord, - prelude::{Chunk, Map}, -}; +use world_generation::{hex_utils::HexCoord, prelude::Map}; -#[derive(Clone)] +#[derive(Clone, Resource)] pub struct NavData { pub tiles: Vec, pub map_height: usize, @@ -14,12 +12,14 @@ pub struct NavData { impl NavData { pub fn get_neighbors(&self, coord: &HexCoord) -> Vec<(HexCoord, OrderedFloat)> { let mut neighbors = Vec::with_capacity(6); + let cur_height = self.get_height(coord); for i in 0..6 { let n = coord.get_neighbor(i); if !self.is_in_bounds(&n) { continue; } - neighbors.push((n, OrderedFloat(1.0))); + let n_height = self.get_height(&n); + neighbors.push((n, OrderedFloat((cur_height - n_height).abs().powi(2)))); } return neighbors; } @@ -27,11 +27,17 @@ impl NavData { return &self.tiles[coord.to_index(self.map_width)]; } + pub fn get_height(&self, coord: &HexCoord) -> f32 { + return self.tiles[coord.to_index(self.map_width)].height; + } + pub fn is_in_bounds(&self, pos: &HexCoord) -> bool { return pos.is_in_bounds(self.map_height, self.map_width); } pub fn build(map: &Map) -> NavData { + #[cfg(feature = "tracing")] + let _path_span = info_span!("Build Nav Data").entered(); let mut tiles = Vec::with_capacity(map.get_tile_count()); let h = map.get_tile_height(); let w = map.get_tile_width(); @@ -54,6 +60,25 @@ impl NavData { map_height: h, }; } + + pub fn update(&mut self, map: &Map) { + #[cfg(feature = "tracing")] + let _path_span = info_span!("Update Nav Data").entered(); + let h = map.get_tile_height(); + let w = map.get_tile_width(); + for y in 0..h { + for x in 0..w { + let coord = HexCoord::from_grid_pos(x, y); + let height = map.sample_height(&coord); + let tile = NavTile { + coord, + height, + move_cost: 1.0, + }; + self.tiles[y * w + x] = tile; + } + } + } } #[derive(Clone)] @@ -65,6 +90,6 @@ pub struct NavTile { impl NavTile { pub fn calculate_heuristic(&self, to: &HexCoord) -> OrderedFloat { - todo!(); + return (self.coord.distance(to) as f32).into(); } } diff --git a/game/units/src/units_debug_plugin.rs b/game/units/src/units_debug_plugin.rs index 160cf2f..ed06830 100644 --- a/game/units/src/units_debug_plugin.rs +++ b/game/units/src/units_debug_plugin.rs @@ -1,10 +1,7 @@ -use std::f32::consts::E; - use bevy::prelude::*; use shared::{resources::TileUnderCursor, sets::GameplaySet, states::AssetLoadState}; -use world_generation::{heightmap, prelude::Map}; -use crate::components::{LandUnit, Target, Unit}; +use crate::components::{LandUnit, Path, Target, Unit}; pub struct UnitsDebugPlugin; @@ -13,6 +10,7 @@ impl Plugin for UnitsDebugPlugin { app.add_systems(Update, init.run_if(in_state(AssetLoadState::Loading))); app.add_systems(Update, (spawn_test_unit, set_unit_target).in_set(GameplaySet)); + app.add_systems(FixedUpdate, (visualize_paths).in_set(GameplaySet)); } } @@ -64,3 +62,21 @@ fn set_unit_target( } } } + +fn visualize_paths(units: Query<&Path, With>, mut gizmos: Gizmos) { + for path in units.iter() { + if path.1 > path.0.len() { + continue; + } + for node in 1..path.0.len() { + let from = path.0[node]; + let to = path.0[node - 1]; + let color = if node > path.1 { + LinearRgba::rgb(1.0, 0.5, 0.0) + } else { + LinearRgba::rgb(1.0, 0.5, 1.5) + }; + gizmos.line(from + Vec3::Y * 0.1, to + Vec3::Y * 0.1, color); + } + } +} diff --git a/game/units/src/units_plugin.rs b/game/units/src/units_plugin.rs index 61571ae..707ec19 100644 --- a/game/units/src/units_plugin.rs +++ b/game/units/src/units_plugin.rs @@ -8,7 +8,7 @@ use world_generation::{hex_utils::HexCoord, prelude::Map}; use crate::{ assets::unit_asset::UnitAssetPlugin, components::{Path, PathTask, Target, Unit}, - nav_data::{self, NavData}, + nav_data::NavData, units_debug_plugin::UnitsDebugPlugin, }; @@ -65,12 +65,15 @@ fn dispatch_path_requests( map: Res, mut commands: Commands, ) { + if units.is_empty() { + return; + } let mut groups: HashMap> = HashMap::new(); for (transform, target, entity) in units.iter() { let req = PathRequest { entity, - to: HexCoord::from_world_pos(transform.translation), + from: HexCoord::from_world_pos(transform.translation), }; if let Some(group) = groups.get_mut(&target.0) { group.push(req); @@ -79,45 +82,94 @@ fn dispatch_path_requests( } } + //todo: only generate when map is changed let nav_data = NavData::build(&map); let pool = AsyncComputeTaskPool::get(); - for (from, units) in groups { + for (target, units) in groups { + let destinations = get_end_points(&target, units.len(), &map); + let mut i = 0; for req in units { let d = nav_data.clone(); + let dst = destinations[i]; + i += 1; let task = pool.spawn(async move { - let path = calculate_path(&from, &req.to, d); - let mut queue = CommandQueue::default(); - queue.push(move |world: &mut World| { - world.entity_mut(req.entity).insert(path); - }); - return queue; + #[cfg(feature = "tracing")] + let _path_span = info_span!("Path Finding").entered(); + if let Some(path) = calculate_path(&req.from, &dst, d) { + let mut queue = CommandQueue::default(); + queue.push(move |world: &mut World| { + world.entity_mut(req.entity).insert(path); + }); + return Some(queue); + } + return None; }); - commands.entity(req.entity).insert(PathTask(task)).remove::(); + commands + .entity(req.entity) + .insert(PathTask(task)) + .remove::() + .remove::(); } } } +fn get_end_points(coord: &HexCoord, count: usize, map: &Map) -> Vec { + let mut result = Vec::with_capacity(count); + if count == 1 { + return vec![*coord]; + } + result.push(*coord); + let mut r = 1; + while result.len() < count { + let tiles = HexCoord::select_ring(coord, r); + let needed = count - result.len(); + if needed >= tiles.len() { + for t in tiles { + if map.is_in_bounds(&t) { + result.push(t); + } + } + } else { + for i in 0..needed { + let t = tiles[i]; + if map.is_in_bounds(&t) { + result.push(t); + } + } + } + r += 1; + } + + return result; +} + fn resolve_path_task(mut tasks: Query<(&mut PathTask, Entity), With>, mut commands: Commands) { for (mut task, entity) in tasks.iter_mut() { - if let Some(mut c) = futures::check_ready(&mut task.0) { - commands.append(&mut c); + if let Some(c) = futures::check_ready(&mut task.0) { + if let Some(mut queue) = c { + commands.append(&mut queue); + } commands.entity(entity).remove::(); } } } -fn calculate_path(from: &HexCoord, to: &HexCoord, nav: NavData) -> Path { +fn calculate_path(from: &HexCoord, to: &HexCoord, nav: NavData) -> Option { let path = astar( from, |n| nav.get_neighbors(n), |n| nav.get(n).calculate_heuristic(to), |n| n == to, ); - todo!("Convert path"); + if let Some((nodes, _cost)) = path { + let result: Vec<_> = nodes.iter().map(|f| f.to_world(nav.get_height(f))).collect(); + return Some(Path(result, 1)); + } + return None; } struct PathRequest { pub entity: Entity, - pub to: HexCoord, + pub from: HexCoord, }