This commit is contained in:
2025-03-12 17:14:35 +01:00
parent 17a2ed676b
commit 709c1762bd
4 changed files with 154 additions and 29 deletions

131
src/aim.rs Normal file
View 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;
}
}

View File

@@ -1,3 +1,4 @@
mod aim;
mod alien;
mod billboards;
mod camera;
@@ -74,6 +75,7 @@ fn main() {
app.add_plugins(platforms::plugin);
app.add_plugins(movables::plugin);
app.add_plugins(billboards::plugin);
app.add_plugins(aim::plugin);
app.insert_resource(AmbientLight {
color: Color::WHITE,

View File

@@ -3,7 +3,6 @@ use std::{f32::consts::PI, time::Duration};
use crate::{
DebugVisuals,
alien::{ALIEN_ASSET_PATH, Animations},
billboards::Billboard,
camera::GameCameraRig,
cash::{Cash, CashCollectEvent},
heads_ui::HeadChanged,
@@ -16,7 +15,6 @@ use bevy::{
window::{CursorGrabMode, PrimaryWindow},
};
use bevy_dolly::prelude::Rig;
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use bevy_tnua::{TnuaUserControlsSystemSet, prelude::*};
use bevy_tnua_avian3d::TnuaAvian3dSensorShape;
@@ -27,10 +25,10 @@ pub struct Player;
struct PlayerAnimations;
#[derive(Component, Default)]
struct PlayerHead;
pub struct PlayerHead;
#[derive(Component, Default)]
struct PlayerRig;
pub struct PlayerRig;
#[derive(Resource, Default)]
struct PlayerSpawned {
@@ -51,7 +49,7 @@ pub fn plugin(app: &mut App) {
(
spawn,
update_camera,
cursor_events,
mouse_rotate,
collect_cash,
toggle_animation,
setup_animations_marker_for_player,
@@ -70,7 +68,6 @@ fn spawn(
asset_server: Res<AssetServer>,
query: Query<&Transform, With<SpawnPoint>>,
mut player_spawned: ResMut<PlayerSpawned>,
mut sprite_params: Sprite3dParams,
) {
if player_spawned.spawned {
return;
@@ -84,7 +81,6 @@ fn spawn(
let mesh = asset_server
.load(GltfAssetLabel::Scene(0).from_asset("models/heads/angry demonstrator.glb"));
let selector = asset_server.load("ui/selector.png");
commands
.spawn((
@@ -113,18 +109,6 @@ fn spawn(
.with_rotation(Quat::from_rotation_y(std::f32::consts::PI))
.with_scale(Vec3::splat(1.5)),
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(ALIEN_ASSET_PATH))),
))
.with_child((
Billboard,
Transform::from_translation(Vec3::new(0., 1.5, 0.)),
Sprite3dBuilder {
image: selector,
pixels_per_metre: 40.,
alpha_mode: AlphaMode::Blend,
unlit: true,
..default()
}
.bundle(&mut sprite_params),
));
commands.spawn((
@@ -135,7 +119,7 @@ fn spawn(
player_spawned.spawned = true;
}
fn cursor_events(
fn mouse_rotate(
mut mouse: EventReader<MouseMotion>,
// todo: Put the player head as a child of the rig to avoid this mess:
mut player: Query<&mut Transform, Or<(With<PlayerRig>, With<PlayerHead>)>>,

View File

@@ -94,6 +94,14 @@ impl EnemySpawn {
};
let this = world.get_entity(entity).unwrap().get::<Self>().unwrap();
let this_transform = world
.get_entity(entity)
.unwrap()
.get::<Transform>()
.unwrap();
let mut this_transform = *this_transform;
this_transform.translation += Vec3::new(0., 1., 0.);
let mesh = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/alien_naked.glb"));
@@ -103,23 +111,23 @@ impl EnemySpawn {
world
.commands()
.entity(entity)
.insert((Name::from("Enemy"), Visibility::default()))
.insert((
this_transform,
Name::from("Enemy"),
Visibility::default(),
RigidBody::Dynamic,
Collider::capsule(0.4, 2.),
LockedAxes::new().lock_rotation_z().lock_rotation_x(),
))
.with_children(|parent| {
parent
.spawn((
Visibility::default(),
RigidBody::Dynamic,
Collider::capsule(0.4, 2.),
LockedAxes::new().lock_rotation_z().lock_rotation_x(),
Transform::from_translation(Vec3::new(0., 1.0, 0.)),
))
.with_child((
Transform::from_translation(Vec3::new(0., 1., 0.)),
SceneRoot(head_mesh),
))
.with_child((
Visibility::default(),
Transform::from_translation(Vec3::new(0., -1.4, 0.))
Transform::from_translation(Vec3::new(0., -2.4, 0.))
.with_scale(Vec3::splat(1.5)),
SceneRoot(mesh),
));