blackout/src/pathfinding.rs

142 lines
4.5 KiB
Rust
Raw Normal View History

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,
&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
}
}