From 0a94c324ac5ce0a1c5b688e57575045f4ed09761 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Mon, 6 Feb 2023 14:22:14 +0100 Subject: [PATCH] Clean up entity chasing API Relates to #266. --- crates/behaviour/src/chase.rs | 91 ++++++++++++++++++---- crates/behaviour/src/lib.rs | 2 +- crates/combat/src/attack.rs | 23 +++--- crates/controller/src/commands/executor.rs | 13 ++-- 4 files changed, 96 insertions(+), 33 deletions(-) diff --git a/crates/behaviour/src/chase.rs b/crates/behaviour/src/chase.rs index 42c912f9..03492153 100644 --- a/crates/behaviour/src/chase.rs +++ b/crates/behaviour/src/chase.rs @@ -7,17 +7,65 @@ pub(crate) struct ChasePlugin; impl Plugin for ChasePlugin { fn build(&self, app: &mut App) { - app.add_system_set_to_stage( - GameStage::Update, - SystemSet::new().with_system(chase.run_in_state(GameState::Playing)), - ); + app.add_event::() + .add_system_to_stage( + GameStage::PreUpdate, + handle_chase_events + .run_in_state(GameState::Playing) + .label(ChaseLabel::ChaseTargetEvent), + ) + .add_system_set_to_stage( + GameStage::Update, + SystemSet::new().with_system(chase.run_in_state(GameState::Playing)), + ); + } +} + +#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemLabel)] +pub enum ChaseLabel { + ChaseTargetEvent, +} + +/// Send this event to start or stop chasing of an entity (movable or static). +pub struct ChaseTargetEvent { + entity: Entity, + target: Option, +} + +impl ChaseTargetEvent { + /// Creates a new chase event. + /// + /// # Arguments + /// + /// * `entity` - the chasing entity. + /// + /// * `target` - target to chase or None if chasing shall be stopped. + pub fn new(entity: Entity, target: Option) -> Self { + Self { entity, target } + } + + fn entity(&self) -> Entity { + self.entity + } + + fn target(&self) -> Option<&ChaseTarget> { + self.target.as_ref() } } /// Units with this component will chase the target entity. -#[derive(Component)] +#[derive(Component, Deref)] +pub struct ChaseTargetComponent(ChaseTarget); + +impl ChaseTargetComponent { + fn new(target: ChaseTarget) -> Self { + Self(target) + } +} + +#[derive(Clone)] pub struct ChaseTarget { - entity: Entity, + target: Entity, min_distance: f32, max_distance: f32, } @@ -27,7 +75,7 @@ impl ChaseTarget { /// /// # Arguments /// - /// * `entity` - entity to chase. + /// * `target` - entity to chase. /// /// * `min_distance` - minimum distance between the chasing entity and the /// cased entity. Elevation is ignored during the distance calculation. @@ -40,7 +88,7 @@ impl ChaseTarget { /// May panic if `min_distance` or `max_distance` is not non-negative /// finite number or when `min_distance` is greater or equal to /// `max_distance`. - pub fn new(entity: Entity, min_distance: f32, max_distance: f32) -> Self { + pub fn new(target: Entity, min_distance: f32, max_distance: f32) -> Self { debug_assert!(min_distance.is_finite()); debug_assert!(max_distance.is_finite()); debug_assert!(min_distance >= 0.); @@ -48,14 +96,14 @@ impl ChaseTarget { debug_assert!(min_distance < max_distance); Self { - entity, + target, min_distance, max_distance, } } - pub fn entity(&self) -> Entity { - self.entity + pub fn target(&self) -> Entity { + self.target } fn min_distance(&self) -> f32 { @@ -67,17 +115,32 @@ impl ChaseTarget { } } +fn handle_chase_events(mut commands: Commands, mut events: EventReader) { + for event in events.iter() { + let mut entity_commands = commands.entity(event.entity()); + match event.target() { + Some(target) => entity_commands.insert(ChaseTargetComponent::new(target.clone())), + None => entity_commands.remove::(), + }; + } +} + fn chase( mut commands: Commands, mut path_events: EventWriter, - chasing: Query<(Entity, &Transform, &ChaseTarget, Option<&PathTarget>)>, + chasing: Query<( + Entity, + &Transform, + &ChaseTargetComponent, + Option<&PathTarget>, + )>, targets: Query<&Transform>, ) { for (entity, transform, chase_target, path_target) in chasing.iter() { - let target_position = match targets.get(chase_target.entity()) { + let target_position = match targets.get(chase_target.target()) { Ok(transform) => transform.translation.to_flat(), Err(_) => { - commands.entity(entity).remove::(); + commands.entity(entity).remove::(); continue; } }; diff --git a/crates/behaviour/src/lib.rs b/crates/behaviour/src/lib.rs index bab971a2..83f07d11 100644 --- a/crates/behaviour/src/lib.rs +++ b/crates/behaviour/src/lib.rs @@ -2,7 +2,7 @@ use bevy::{app::PluginGroupBuilder, prelude::PluginGroup}; use chase::ChasePlugin; -pub use chase::ChaseTarget; +pub use chase::{ChaseLabel, ChaseTarget, ChaseTargetComponent, ChaseTargetEvent}; mod chase; diff --git a/crates/combat/src/attack.rs b/crates/combat/src/attack.rs index 8186a247..7c5a9003 100644 --- a/crates/combat/src/attack.rs +++ b/crates/combat/src/attack.rs @@ -1,7 +1,7 @@ use std::{cmp::Ordering, collections::BinaryHeap}; use bevy::prelude::*; -use de_behaviour::ChaseTarget; +use de_behaviour::{ChaseLabel, ChaseTarget, ChaseTargetComponent, ChaseTargetEvent}; use de_core::{objects::ObjectType, stages::GameStage, state::GameState}; use de_objects::{ColliderCache, LaserCannon, ObjectCache}; use iyes_loopless::prelude::*; @@ -24,7 +24,9 @@ impl Plugin for AttackPlugin { app.add_event::() .add_system_to_stage( GameStage::PreUpdate, - attack.run_in_state(GameState::Playing), + attack + .run_in_state(GameState::Playing) + .before(ChaseLabel::ChaseTargetEvent), ) .add_system_set_to_stage( GameStage::Update, @@ -67,17 +69,18 @@ impl AttackEvent { struct Attacking; fn attack( - mut commands: Commands, - mut events: EventReader, + mut attack_events: EventReader, cannons: Query<&LaserCannon>, + mut chase_events: EventWriter, ) { - for event in events.iter() { + for event in attack_events.iter() { if let Ok(cannon) = cannons.get(event.attacker()) { - commands.entity(event.attacker()).insert(ChaseTarget::new( + let target = ChaseTarget::new( event.enemy(), MIN_CHASE_DISTNACE * cannon.range(), MAX_CHASE_DISTNACE * cannon.range(), - )); + ); + chase_events.send(ChaseTargetEvent::new(event.attacker(), Some(target))); } } } @@ -95,7 +98,7 @@ fn aim_and_fire( Entity, &Transform, &mut LaserCannon, - &ChaseTarget, + &ChaseTargetComponent, Option<&Attacking>, )>, targets: Query<(&Transform, &ObjectType)>, @@ -108,7 +111,7 @@ fn aim_and_fire( let mut fire_queue = BinaryHeap::new(); for (attacker, attacker_transform, mut cannon, target, marker) in attackers { - let target_position = match targets.get(target.entity()) { + let target_position = match targets.get(target.target()) { Ok((transform, &object_type)) => { let centroid: Vec3 = cache.get_collider(object_type).aabb().center().into(); transform.translation + centroid @@ -124,7 +127,7 @@ fn aim_and_fire( let aims_at_target = sightline .sight(&ray, cannon.range(), attacker) .entity() - .map_or(true, |e| e != target.entity()); + .map_or(true, |e| e != target.target()); if aims_at_target { if marker.is_some() { diff --git a/crates/controller/src/commands/executor.rs b/crates/controller/src/commands/executor.rs index 3a63c81c..624176d9 100644 --- a/crates/controller/src/commands/executor.rs +++ b/crates/controller/src/commands/executor.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use de_behaviour::ChaseTarget; +use de_behaviour::ChaseTargetEvent; use de_combat::AttackEvent; use de_core::{objects::MovableSolid, stages::GameStage, state::AppState}; use de_pathing::{PathQueryProps, PathTarget, UpdateEntityPath}; @@ -67,17 +67,14 @@ impl GroupAttackEvent { type SelectedMovable = (With, With); fn send_selected_system( - mut commands: Commands, mut send_events: EventReader, - selected: Query<(Entity, Option<&ChaseTarget>), SelectedMovable>, + selected: Query, mut path_events: EventWriter, + mut chase_events: EventWriter, ) { if let Some(send) = send_events.iter().last() { - for (entity, chase) in selected.iter() { - if chase.is_some() { - commands.entity(entity).remove::(); - } - + for entity in selected.iter() { + chase_events.send(ChaseTargetEvent::new(entity, None)); path_events.send(UpdateEntityPath::new( entity, PathTarget::new(send.target(), PathQueryProps::exact(), false),