parent
6900d1ce31
commit
1ebc94bd9d
File diff suppressed because one or more lines are too long
@ -0,0 +1,104 @@
|
||||
use std::f32::consts::PI;
|
||||
use std::ops::RangeInclusive;
|
||||
use rand::Rng;
|
||||
use bevy::prelude::*;
|
||||
use heron::prelude::*;
|
||||
use crate::{
|
||||
map::GroundContact,
|
||||
player::Player,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Enemy {
|
||||
pub rotation: f32,
|
||||
pub bounds: (RangeInclusive<f32>, RangeInclusive<f32>),
|
||||
pub patrol_target: Option<(f32, f32)>,
|
||||
}
|
||||
|
||||
pub fn walk(
|
||||
time: Res<Time>,
|
||||
mut queries: QuerySet<(
|
||||
QueryState<&Transform, With<Player>>,
|
||||
QueryState<(&mut Transform, &mut Enemy, &mut Velocity, &GroundContact)>
|
||||
)>,
|
||||
) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let player_positions = queries.q0()
|
||||
.iter()
|
||||
.map(|player_transform|
|
||||
player_transform.translation
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut enemies = queries.q1();
|
||||
for (mut transform, mut enemy, mut velocity, contact) in enemies.iter_mut() {
|
||||
if contact.0 == 0 {
|
||||
// do not move unless on ground
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: if transform is out of enemy.bounds, attack any player
|
||||
// TODO: do not patrol if player is near :)
|
||||
|
||||
// find nearest player in bounds
|
||||
let mut target = None;
|
||||
let mut nearest_distance = None;
|
||||
for player_position in &player_positions {
|
||||
if enemy.bounds.0.contains(&player_position.x) && enemy.bounds.1.contains(&player_position.z) {
|
||||
let way = *player_position - transform.translation;
|
||||
let distance = way.length_squared();
|
||||
if nearest_distance.map_or(true, |nearest_distance| distance < nearest_distance) {
|
||||
target = Some(*player_position);
|
||||
nearest_distance = Some(distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if target.is_none() {
|
||||
// patrol
|
||||
let x_bounds = enemy.bounds.0.clone();
|
||||
let z_bounds = enemy.bounds.1.clone();
|
||||
let (x, z) = enemy.patrol_target
|
||||
// new patrol target
|
||||
.get_or_insert_with(|| (
|
||||
rng.gen_range(x_bounds),
|
||||
rng.gen_range(z_bounds)
|
||||
)).clone();
|
||||
if Vec2::new(transform.translation.x - x,
|
||||
transform.translation.z - z)
|
||||
.length_squared() < 1.0
|
||||
{
|
||||
// target reached, set new one next frame
|
||||
enemy.patrol_target = None;
|
||||
} else {
|
||||
// continue patrolling
|
||||
target = Some(Vec3::new(x, 0.0, z));
|
||||
}
|
||||
} else {
|
||||
// if there had been a target,
|
||||
// reset previous patrolling
|
||||
enemy.patrol_target = None;
|
||||
}
|
||||
|
||||
if let Some(direction) = target.and_then(|target| {
|
||||
let way = target - transform.translation;
|
||||
Vec3::new(way.x, 0.0, way.z)
|
||||
.try_normalize()
|
||||
})
|
||||
{
|
||||
let target_rotation = Vec2::new(direction.x, direction.z)
|
||||
.angle_between(Vec2::Y);
|
||||
let rotation_delta = 2.0 * PI * time.delta_seconds() * if target_rotation > enemy.rotation { 1.0 } else { -1.0 };
|
||||
if rotation_delta.abs() < (target_rotation - enemy.rotation).abs() {
|
||||
// rotate before move
|
||||
enemy.rotation += rotation_delta;
|
||||
} else {
|
||||
enemy.rotation = target_rotation;
|
||||
// move
|
||||
velocity.linear = 3.0 * direction + 2.0 * Vec3::Y;
|
||||
}
|
||||
transform.rotation = Quat::from_rotation_y(enemy.rotation);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue