mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Smoother pathfinding for fast animals
This commit is contained in:
committed by
jshipsey
parent
efad612497
commit
fb8ab18346
@ -78,7 +78,11 @@ impl Component for Agent {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Activity {
|
pub enum Activity {
|
||||||
Idle(Vec2<f32>),
|
Idle(Vec2<f32>),
|
||||||
Follow(EcsEntity, Chaser),
|
Follow {
|
||||||
|
target: EcsEntity,
|
||||||
|
chaser: Chaser,
|
||||||
|
move_dir: Vec2<f32>,
|
||||||
|
},
|
||||||
Attack {
|
Attack {
|
||||||
target: EcsEntity,
|
target: EcsEntity,
|
||||||
chaser: Chaser,
|
chaser: Chaser,
|
||||||
@ -91,7 +95,7 @@ pub enum Activity {
|
|||||||
impl Activity {
|
impl Activity {
|
||||||
pub fn is_follow(&self) -> bool {
|
pub fn is_follow(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Activity::Follow(_, _) => true,
|
Activity::Follow { .. } => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,9 +77,9 @@ impl Route {
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let next_tgt = next.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0);
|
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))
|
if pos.xy().distance_squared(next_tgt.xy()) < traversal_tolerance.powf(2.0)
|
||||||
.magnitude_squared()
|
&& next_tgt.z - pos.z < 0.2
|
||||||
< (traversal_tolerance * 2.0).powf(2.0)
|
&& next_tgt.z - pos.z > -2.2
|
||||||
{
|
{
|
||||||
self.next_idx += 1;
|
self.next_idx += 1;
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ impl Route {
|
|||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct Chaser {
|
pub struct Chaser {
|
||||||
last_search_tgt: Option<Vec3<f32>>,
|
last_search_tgt: Option<Vec3<f32>>,
|
||||||
route: Route,
|
route: Option<Route>,
|
||||||
/// We use this hasher (AAHasher) because:
|
/// We use this hasher (AAHasher) because:
|
||||||
/// (1) we care about DDOS attacks (ruling out FxHash);
|
/// (1) we care about DDOS attacks (ruling out FxHash);
|
||||||
/// (2) we don't care about determinism across computers (we can use
|
/// (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);
|
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;
|
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);
|
let end_to_tgt = end.map(|e| e as f32).distance(tgt);
|
||||||
if end_to_tgt > pos_to_tgt * 0.3 + 5.0 {
|
if end_to_tgt > pos_to_tgt * 0.3 + 5.0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
if thread_rng().gen::<f32>() < 0.005 {
|
if thread_rng().gen::<f32>() < 0.005 {
|
||||||
// TODO: Only re-calculate route when we're stuck
|
None
|
||||||
self.route = Route::default();
|
} 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 {
|
} else {
|
||||||
None
|
None
|
||||||
@ -144,7 +148,12 @@ impl Chaser {
|
|||||||
.map(|last_tgt| last_tgt.distance(tgt) > pos_to_tgt * 0.15 + 5.0)
|
.map(|last_tgt| last_tgt.distance(tgt) > pos_to_tgt * 0.15 + 5.0)
|
||||||
.unwrap_or(true)
|
.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))
|
Some((tgt - pos) * Vec3::new(1.0, 1.0, 0.0))
|
||||||
@ -158,7 +167,7 @@ fn find_path<V>(
|
|||||||
vol: &V,
|
vol: &V,
|
||||||
startf: Vec3<f32>,
|
startf: Vec3<f32>,
|
||||||
endf: Vec3<f32>,
|
endf: Vec3<f32>,
|
||||||
) -> Path<Vec3<i32>>
|
) -> (Vec3<f32>, Option<Path<Vec3<i32>>>)
|
||||||
where
|
where
|
||||||
V: BaseVol<Vox = Block> + ReadVol,
|
V: BaseVol<Vox = Block> + ReadVol,
|
||||||
{
|
{
|
||||||
@ -192,7 +201,7 @@ where
|
|||||||
get_walkable_z(endf.map(|e| e.floor() as i32)),
|
get_walkable_z(endf.map(|e| e.floor() as i32)),
|
||||||
) {
|
) {
|
||||||
(Some(start), Some(end)) => (start, end),
|
(Some(start), Some(end)) => (start, end),
|
||||||
_ => return Path::default(),
|
_ => return (startf, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let heuristic = |pos: &Vec3<i32>| (pos.distance_squared(end) as f32).sqrt();
|
let heuristic = |pos: &Vec3<i32>| (pos.distance_squared(end) as f32).sqrt();
|
||||||
@ -240,6 +249,7 @@ where
|
|||||||
.map(move |dir| (pos, dir))
|
.map(move |dir| (pos, dir))
|
||||||
.filter(move |(pos, dir)| {
|
.filter(move |(pos, dir)| {
|
||||||
is_walkable(pos)
|
is_walkable(pos)
|
||||||
|
&& is_walkable(&(*pos + **dir))
|
||||||
&& ((dir.z < 1
|
&& ((dir.z < 1
|
||||||
|| vol
|
|| vol
|
||||||
.get(pos + Vec3::unit_z() * 2)
|
.get(pos + Vec3::unit_z() * 2)
|
||||||
@ -266,34 +276,32 @@ where
|
|||||||
.map(move |(dir, _)| pos + *dir),
|
.map(move |(dir, _)| pos + *dir),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let transition = |a: &Vec3<i32>, b: &Vec3<i32>| {
|
let transition =
|
||||||
((*a - *b) * Vec3::new(1, 1, 3)).map(|e| e.abs()).reduce_max() as f32
|
|a: &Vec3<i32>, b: &Vec3<i32>| 1.0 + endf.distance((*b).map(|e| e as f32 + 0.5)) * 0.02;
|
||||||
+ endf.distance((*b).map(|e| e as f32)) * 0.01
|
|
||||||
};
|
|
||||||
let satisfied = |pos: &Vec3<i32>| pos == &end;
|
let satisfied = |pos: &Vec3<i32>| pos == &end;
|
||||||
|
|
||||||
let mut new_astar = match astar.take() {
|
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,
|
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);
|
*astar = Some(new_astar);
|
||||||
|
|
||||||
match path_result {
|
(startf, match path_result {
|
||||||
PathResult::Path(path) => {
|
PathResult::Path(path) => {
|
||||||
*astar = None;
|
*astar = None;
|
||||||
path
|
Some(path)
|
||||||
},
|
},
|
||||||
PathResult::None(path) => {
|
PathResult::None(path) => {
|
||||||
*astar = None;
|
*astar = None;
|
||||||
path
|
Some(path)
|
||||||
},
|
},
|
||||||
PathResult::Exhausted(path) => {
|
PathResult::Exhausted(path) => {
|
||||||
*astar = None;
|
*astar = None;
|
||||||
path
|
Some(path)
|
||||||
},
|
},
|
||||||
PathResult::Pending => Path::default(),
|
PathResult::Pending => None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
item::{Hands, ItemKind, Tool},
|
item::{Hands, ItemKind, Tool},
|
||||||
CharacterState, StateUpdate,
|
Body, CharacterState, StateUpdate,
|
||||||
Body,
|
|
||||||
},
|
},
|
||||||
event::LocalEvent,
|
event::LocalEvent,
|
||||||
states::*,
|
states::*,
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
agent::Activity,
|
agent::Activity,
|
||||||
item::{tool::ToolKind, ItemKind},
|
item::{tool::ToolKind, ItemKind},
|
||||||
Agent, Alignment, CharacterState, ChatMsg, ControlAction, Controller, Loadout, MountState,
|
Agent, Alignment, CharacterState, ChatMsg, ControlAction, Controller, Loadout, MountState,
|
||||||
Ori, Pos, Scale, Stats, Vel,
|
Ori, Pos, Scale, SpeechBubble, Stats, Vel,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
path::Chaser,
|
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
|
// and so can afford to be less precise when trying to move around
|
||||||
// the world (especially since they would otherwise get stuck on
|
// the world (especially since they would otherwise get stuck on
|
||||||
// obstacles that smaller entities would not).
|
// 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 do_idle = false;
|
||||||
let mut choose_target = false;
|
let mut choose_target = false;
|
||||||
@ -187,7 +187,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
choose_target = true;
|
choose_target = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Activity::Follow(target, chaser) => {
|
Activity::Follow {
|
||||||
|
target,
|
||||||
|
chaser,
|
||||||
|
move_dir,
|
||||||
|
} => {
|
||||||
if let (Some(tgt_pos), _tgt_stats) =
|
if let (Some(tgt_pos), _tgt_stats) =
|
||||||
(positions.get(*target), stats.get(*target))
|
(positions.get(*target), stats.get(*target))
|
||||||
{
|
{
|
||||||
@ -201,10 +205,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
AVG_FOLLOW_DIST,
|
AVG_FOLLOW_DIST,
|
||||||
traversal_tolerance,
|
traversal_tolerance,
|
||||||
) {
|
) {
|
||||||
inputs.move_dir = Vec2::from(bearing)
|
*move_dir = 0.9f32 * *move_dir
|
||||||
|
+ 0.1f32
|
||||||
|
* Vec2::from(bearing)
|
||||||
.try_normalized()
|
.try_normalized()
|
||||||
.unwrap_or(Vec2::zero());
|
.unwrap_or(Vec2::zero());
|
||||||
inputs.jump.set_state(bearing.z > 1.0);
|
inputs.move_dir = *move_dir;
|
||||||
|
inputs.jump.set_state(bearing.z > 1.5);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
do_idle = true;
|
do_idle = true;
|
||||||
@ -317,7 +324,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
inputs.move_dir = Vec2::from(bearing)
|
inputs.move_dir = Vec2::from(bearing)
|
||||||
.try_normalized()
|
.try_normalized()
|
||||||
.unwrap_or(Vec2::zero());
|
.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)
|
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) {
|
if let Some(owner_pos) = positions.get(owner) {
|
||||||
let dist_sqrd = pos.0.distance_squared(owner_pos.0);
|
let dist_sqrd = pos.0.distance_squared(owner_pos.0);
|
||||||
if dist_sqrd > MAX_FOLLOW_DIST.powf(2.0) && !agent.activity.is_follow() {
|
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
|
// Attack owner's attacker
|
||||||
|
Reference in New Issue
Block a user