From 8a0b7fd173daa27983997c7ead21f31dc8c03051 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 4 Jul 2020 01:17:51 +0100 Subject: [PATCH] Smoother pathfinding for fast animals --- common/src/comp/agent.rs | 8 ++++-- common/src/path.rs | 58 ++++++++++++++++++++++---------------- common/src/states/utils.rs | 3 +- common/src/sys/agent.rs | 29 +++++++++++++------ 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 49c183c34c..cda306333d 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -78,7 +78,11 @@ impl Component for Agent { #[derive(Clone, Debug)] pub enum Activity { Idle(Vec2), - Follow(EcsEntity, Chaser), + Follow { + target: EcsEntity, + chaser: Chaser, + move_dir: Vec2, + }, Attack { target: EcsEntity, chaser: Chaser, @@ -91,7 +95,7 @@ pub enum Activity { impl Activity { pub fn is_follow(&self) -> bool { match self { - Activity::Follow(_, _) => true, + Activity::Follow { .. } => true, _ => false, } } diff --git a/common/src/path.rs b/common/src/path.rs index 274df33510..0179e8f332 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -77,9 +77,9 @@ impl Route { None } else { let next_tgt = next.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); - if ((pos - (next_tgt + Vec3::unit_z() * 0.5)) * Vec3::new(1.0, 1.0, 0.3)) - .magnitude_squared() - < (traversal_tolerance * 2.0).powf(2.0) + if pos.xy().distance_squared(next_tgt.xy()) < traversal_tolerance.powf(2.0) + && next_tgt.z - pos.z < 0.2 + && next_tgt.z - pos.z > -2.2 { self.next_idx += 1; } @@ -93,7 +93,7 @@ impl Route { #[derive(Default, Clone, Debug)] pub struct Chaser { last_search_tgt: Option>, - route: Route, + route: Option, /// We use this hasher (AAHasher) because: /// (1) we care about DDOS attacks (ruling out FxHash); /// (2) we don't care about determinism across computers (we can use @@ -115,21 +115,25 @@ impl Chaser { { let pos_to_tgt = pos.distance(tgt); - if ((pos - tgt) * Vec3::new(1.0, 1.0, 0.15)).magnitude_squared() < min_dist.powf(2.0) { + if ((pos - tgt) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared() < min_dist.powf(2.0) { return None; } - let bearing = if let Some(end) = self.route.path().end().copied() { + let bearing = if let Some(end) = self.route.as_ref().and_then(|r| r.path().end().copied()) { let end_to_tgt = end.map(|e| e as f32).distance(tgt); if end_to_tgt > pos_to_tgt * 0.3 + 5.0 { None } else { if thread_rng().gen::() < 0.005 { - // TODO: Only re-calculate route when we're stuck - self.route = Route::default(); + None + } else { + self.route + .as_mut() + .and_then(|r| r.traverse(vol, pos, traversal_tolerance)) + .filter(|b| { + b.xy().magnitude_squared() < (traversal_tolerance + 1.0).powf(2.0) + }) } - - self.route.traverse(vol, pos, traversal_tolerance) } } else { None @@ -144,7 +148,12 @@ impl Chaser { .map(|last_tgt| last_tgt.distance(tgt) > pos_to_tgt * 0.15 + 5.0) .unwrap_or(true) { - self.route = find_path(&mut self.astar, vol, pos, tgt).into(); + let (start_pos, path) = find_path(&mut self.astar, vol, pos, tgt); + if start_pos.distance_squared(pos) < 4.0f32.powf(2.0) { + self.route = path.map(Route::from); + } else { + self.route = None; + } } Some((tgt - pos) * Vec3::new(1.0, 1.0, 0.0)) @@ -158,7 +167,7 @@ fn find_path( vol: &V, startf: Vec3, endf: Vec3, -) -> Path> +) -> (Vec3, Option>>) where V: BaseVol + ReadVol, { @@ -192,7 +201,7 @@ where get_walkable_z(endf.map(|e| e.floor() as i32)), ) { (Some(start), Some(end)) => (start, end), - _ => return Path::default(), + _ => return (startf, None), }; let heuristic = |pos: &Vec3| (pos.distance_squared(end) as f32).sqrt(); @@ -240,6 +249,7 @@ where .map(move |dir| (pos, dir)) .filter(move |(pos, dir)| { is_walkable(pos) + && is_walkable(&(*pos + **dir)) && ((dir.z < 1 || vol .get(pos + Vec3::unit_z() * 2) @@ -266,34 +276,32 @@ where .map(move |(dir, _)| pos + *dir), ) }; - let transition = |a: &Vec3, b: &Vec3| { - ((*a - *b) * Vec3::new(1, 1, 3)).map(|e| e.abs()).reduce_max() as f32 - + endf.distance((*b).map(|e| e as f32)) * 0.01 - }; + let transition = + |a: &Vec3, b: &Vec3| 1.0 + endf.distance((*b).map(|e| e as f32 + 0.5)) * 0.02; let satisfied = |pos: &Vec3| pos == &end; let mut new_astar = match astar.take() { - None => Astar::new(20_000, start, heuristic, DefaultHashBuilder::default()), + None => Astar::new(25_000, start, heuristic, DefaultHashBuilder::default()), Some(astar) => astar, }; - let path_result = new_astar.poll(60, heuristic, neighbors, transition, satisfied); + let path_result = new_astar.poll(100, heuristic, neighbors, transition, satisfied); *astar = Some(new_astar); - match path_result { + (startf, match path_result { PathResult::Path(path) => { *astar = None; - path + Some(path) }, PathResult::None(path) => { *astar = None; - path + Some(path) }, PathResult::Exhausted(path) => { *astar = None; - path + Some(path) }, - PathResult::Pending => Path::default(), - } + PathResult::Pending => None, + }) } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index f42a9e1b87..229f08f60d 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1,8 +1,7 @@ use crate::{ comp::{ item::{Hands, ItemKind, Tool}, - CharacterState, StateUpdate, - Body, + Body, CharacterState, StateUpdate, }, event::LocalEvent, states::*, diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 6be9618582..9d9922d057 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -4,7 +4,7 @@ use crate::{ agent::Activity, item::{tool::ToolKind, ItemKind}, Agent, Alignment, CharacterState, ChatMsg, ControlAction, Controller, Loadout, MountState, - Ori, Pos, Scale, Stats, Vel, + Ori, Pos, Scale, SpeechBubble, Stats, Vel, }, event::{EventBus, ServerEvent}, path::Chaser, @@ -126,7 +126,7 @@ impl<'a> System<'a> for Sys { // and so can afford to be less precise when trying to move around // the world (especially since they would otherwise get stuck on // obstacles that smaller entities would not). - let traversal_tolerance = scale + vel.0.magnitude() * 0.35; + let traversal_tolerance = scale + vel.0.magnitude() * 0.25; let mut do_idle = false; let mut choose_target = false; @@ -187,7 +187,11 @@ impl<'a> System<'a> for Sys { choose_target = true; } }, - Activity::Follow(target, chaser) => { + Activity::Follow { + target, + chaser, + move_dir, + } => { if let (Some(tgt_pos), _tgt_stats) = (positions.get(*target), stats.get(*target)) { @@ -201,10 +205,13 @@ impl<'a> System<'a> for Sys { AVG_FOLLOW_DIST, traversal_tolerance, ) { - inputs.move_dir = Vec2::from(bearing) - .try_normalized() - .unwrap_or(Vec2::zero()); - inputs.jump.set_state(bearing.z > 1.0); + *move_dir = 0.9f32 * *move_dir + + 0.1f32 + * Vec2::from(bearing) + .try_normalized() + .unwrap_or(Vec2::zero()); + inputs.move_dir = *move_dir; + inputs.jump.set_state(bearing.z > 1.5); } } else { do_idle = true; @@ -317,7 +324,7 @@ impl<'a> System<'a> for Sys { inputs.move_dir = Vec2::from(bearing) .try_normalized() .unwrap_or(Vec2::zero()); - inputs.jump.set_state(bearing.z > 1.0); + inputs.jump.set_state(bearing.z > 1.5); } if dist_sqrd < 16.0f32.powf(2.0) @@ -419,7 +426,11 @@ impl<'a> System<'a> for Sys { if let Some(owner_pos) = positions.get(owner) { let dist_sqrd = pos.0.distance_squared(owner_pos.0); if dist_sqrd > MAX_FOLLOW_DIST.powf(2.0) && !agent.activity.is_follow() { - agent.activity = Activity::Follow(owner, Chaser::default()); + agent.activity = Activity::Follow { + target: owner, + chaser: Chaser::default(), + move_dir: Vec2::zero(), + }; } // Attack owner's attacker