2021-05-13 17:25:45 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use bevy::{prelude::*, tasks::prelude::*};
|
2021-06-15 22:48:49 +00:00
|
|
|
use bevy_rapier2d::{na::UnitComplex, prelude::*};
|
2021-05-13 17:25:45 +00:00
|
|
|
use crossbeam_channel::{unbounded, Receiver};
|
|
|
|
use derive_more::{Deref, DerefMut};
|
|
|
|
use pathfinding::prelude::*;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
core::{Coordinates, PointLike},
|
|
|
|
map::Map,
|
2021-06-02 22:02:38 +00:00
|
|
|
navigation::{RotationSpeed, Speed},
|
2021-05-13 17:25:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default, Deref, DerefMut, Eq, Hash, PartialEq, Reflect)]
|
|
|
|
#[reflect(Component)]
|
|
|
|
pub struct Destination(pub (i32, i32));
|
|
|
|
|
|
|
|
impl_pointlike_for_tuple_component!(Destination);
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)]
|
|
|
|
#[reflect(Component)]
|
|
|
|
pub struct Path(pub Vec<(i32, i32)>);
|
|
|
|
|
|
|
|
pub fn find_path(
|
|
|
|
start: &dyn PointLike,
|
|
|
|
destination: &dyn PointLike,
|
|
|
|
map: &Map,
|
|
|
|
) -> Option<(Vec<(i32, i32)>, u32)> {
|
|
|
|
astar(
|
|
|
|
&start.into(),
|
|
|
|
|p| {
|
|
|
|
let mut successors: Vec<((i32, i32), u32)> = vec![];
|
2021-06-09 19:53:48 +00:00
|
|
|
for tile in map.get_available_exits(p.0 as usize, p.1 as usize) {
|
2021-05-13 17:25:45 +00:00
|
|
|
successors.push(((tile.0 as i32, tile.1 as i32), (tile.2 * 100.) as u32));
|
|
|
|
}
|
|
|
|
successors
|
|
|
|
},
|
|
|
|
|p| (p.distance_squared(destination) * 100.) as u32,
|
|
|
|
|p| *p == destination.into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calculate_path(
|
|
|
|
mut commands: Commands,
|
|
|
|
pool: Res<AsyncComputeTaskPool>,
|
|
|
|
mut calculating: Local<HashMap<Entity, Receiver<Path>>>,
|
|
|
|
query: Query<(Entity, &Destination, &Coordinates), Changed<Destination>>,
|
|
|
|
destinations: Query<&Destination>,
|
|
|
|
map: Query<&Map>,
|
|
|
|
) {
|
|
|
|
let calculating_clone = calculating.clone();
|
|
|
|
for (entity, rx) in calculating_clone.iter() {
|
|
|
|
if destinations.get(*entity).is_ok() {
|
|
|
|
if let Ok(path) = rx.try_recv() {
|
|
|
|
commands.entity(*entity).insert(path);
|
|
|
|
calculating.remove(&entity);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
calculating.remove(&entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (entity, destination, coordinates) in query.iter() {
|
|
|
|
if !calculating.contains_key(&entity) {
|
|
|
|
let (tx, rx) = unbounded();
|
|
|
|
calculating.insert(entity, rx);
|
|
|
|
for map in map.iter() {
|
|
|
|
let start_clone = *coordinates;
|
|
|
|
let destination_clone = *destination;
|
|
|
|
let map_clone = map.clone();
|
|
|
|
let tx_clone = tx.clone();
|
|
|
|
pool.spawn(async move {
|
|
|
|
if let Some(result) = find_path(&start_clone, &destination_clone, &map_clone) {
|
|
|
|
tx_clone.send(Path(result.0)).expect("Channel should exist");
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.detach();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 22:48:49 +00:00
|
|
|
fn negotiate_path(
|
2021-05-13 17:25:45 +00:00
|
|
|
mut commands: Commands,
|
|
|
|
mut query: Query<(
|
|
|
|
Entity,
|
|
|
|
&mut Path,
|
2021-06-15 22:48:49 +00:00
|
|
|
&mut RigidBodyPosition,
|
2021-06-02 21:53:35 +00:00
|
|
|
&mut RigidBodyVelocity,
|
2021-05-13 17:25:45 +00:00
|
|
|
&Speed,
|
|
|
|
Option<&RotationSpeed>,
|
|
|
|
)>,
|
|
|
|
) {
|
2021-06-15 22:48:49 +00:00
|
|
|
for (entity, mut path, mut position, mut velocity, speed, rotation_speed) in query.iter_mut() {
|
|
|
|
let mut new_path = path.0.clone();
|
|
|
|
let start_i32 = (
|
|
|
|
position.position.translation.x,
|
|
|
|
position.position.translation.y,
|
|
|
|
)
|
|
|
|
.i32();
|
|
|
|
let new_path_clone = new_path.clone();
|
|
|
|
let mut iter = new_path_clone.split(|p| *p == start_i32);
|
|
|
|
if iter.next().is_some() {
|
|
|
|
if let Some(upcoming) = iter.next() {
|
|
|
|
new_path = vec![start_i32];
|
|
|
|
new_path.append(&mut upcoming.to_vec());
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
2021-06-15 22:48:49 +00:00
|
|
|
}
|
|
|
|
**path = new_path;
|
|
|
|
if path.len() >= 2 {
|
|
|
|
let start = Vec2::new(
|
|
|
|
position.position.translation.x,
|
|
|
|
position.position.translation.y,
|
|
|
|
);
|
|
|
|
let next = path[1];
|
|
|
|
let next = Vec2::new(next.0 as f32, next.1 as f32);
|
|
|
|
if rotation_speed.is_some() {
|
|
|
|
let v = next - start;
|
|
|
|
let angle = v.y.atan2(v.x);
|
|
|
|
position.position.rotation = UnitComplex::new(angle);
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
2021-06-15 22:48:49 +00:00
|
|
|
let mut direction = next - start;
|
|
|
|
direction = direction.normalize();
|
|
|
|
direction *= speed.0;
|
|
|
|
velocity.linvel = direction.into();
|
|
|
|
} else {
|
|
|
|
commands.entity(entity).remove::<Path>();
|
|
|
|
commands.entity(entity).remove::<Destination>();
|
|
|
|
velocity.linvel = Vec2::ZERO.into();
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-15 22:48:49 +00:00
|
|
|
}
|
2021-05-13 17:25:45 +00:00
|
|
|
|
|
|
|
pub struct PathfindingPlugin;
|
|
|
|
|
|
|
|
impl Plugin for PathfindingPlugin {
|
|
|
|
fn build(&self, app: &mut AppBuilder) {
|
2021-06-15 22:48:49 +00:00
|
|
|
app.add_system_to_stage(CoreStage::PostUpdate, calculate_path.system())
|
|
|
|
.add_system(negotiate_path.system());
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
|
|
|
}
|