auto aim
This commit is contained in:
131
src/aim.rs
Normal file
131
src/aim.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use crate::{
|
||||
billboards::Billboard,
|
||||
player::{Player, PlayerHead},
|
||||
tb_entities::EnemySpawn,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
#[derive(Resource, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
struct AimState {
|
||||
pub target: Option<Entity>,
|
||||
pub range: f32,
|
||||
pub angle: f32,
|
||||
}
|
||||
|
||||
impl Default for AimState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
target: None,
|
||||
range: 40.,
|
||||
angle: PI / 10.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
struct Marker;
|
||||
|
||||
#[derive(Event)]
|
||||
enum MarkerEvent {
|
||||
Spawn(Entity),
|
||||
Despawn,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.init_resource::<AimState>();
|
||||
app.add_systems(Update, update);
|
||||
app.add_observer(marker_event);
|
||||
}
|
||||
|
||||
fn marker_event(
|
||||
trigger: Trigger<MarkerEvent>,
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut sprite_params: Sprite3dParams,
|
||||
marker: Query<Entity, With<Marker>>,
|
||||
) {
|
||||
for m in marker.iter() {
|
||||
commands.entity(m).despawn_recursive();
|
||||
}
|
||||
|
||||
let MarkerEvent::Spawn(target) = trigger.event() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let selector = asset_server.load("ui/selector.png");
|
||||
|
||||
let id = commands
|
||||
.spawn((
|
||||
Name::new("aim-marker"),
|
||||
Billboard,
|
||||
Marker,
|
||||
Transform::from_translation(Vec3::new(0., 3., 0.)),
|
||||
Sprite3dBuilder {
|
||||
image: selector,
|
||||
pixels_per_metre: 30.,
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
unlit: true,
|
||||
..default()
|
||||
}
|
||||
.bundle(&mut sprite_params),
|
||||
))
|
||||
.id();
|
||||
|
||||
commands.entity(*target).add_child(id);
|
||||
}
|
||||
|
||||
fn update(
|
||||
mut commands: Commands,
|
||||
mut state: ResMut<AimState>,
|
||||
query: Query<(Entity, &Transform), With<EnemySpawn>>,
|
||||
player_pos: Query<&Transform, With<Player>>,
|
||||
player_rot: Query<&Transform, With<PlayerHead>>,
|
||||
) {
|
||||
let Some(player_pos) = player_pos.iter().next().map(|t| t.translation) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(player_forward) = player_rot.iter().next().map(|t| t.forward()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut new_target = None;
|
||||
let mut target_distance = f32::MAX;
|
||||
|
||||
for (e, t) in query.iter() {
|
||||
let delta = player_pos - t.translation;
|
||||
|
||||
let distance = delta.length();
|
||||
|
||||
if distance > state.range {
|
||||
continue;
|
||||
}
|
||||
|
||||
let angle = player_forward.angle_between(delta.normalize());
|
||||
|
||||
if angle < state.angle && distance < target_distance {
|
||||
new_target = Some(e);
|
||||
target_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(e) = state.target {
|
||||
if commands.get_entity(e).is_none() {
|
||||
state.target = None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if new_target != state.target {
|
||||
if let Some(target) = new_target {
|
||||
commands.trigger(MarkerEvent::Spawn(target));
|
||||
} else {
|
||||
commands.trigger(MarkerEvent::Despawn);
|
||||
}
|
||||
state.target = new_target;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user