bevy_steering/behaviors/
approach.rs1use avian3d::prelude::*;
2use bevy::{
3 ecs::{lifecycle::HookContext, query::QueryData, world::DeferredWorld},
4 prelude::*,
5};
6#[cfg(feature = "serialize")]
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 agent::SteeringAgent,
11 control::{BehaviorType, SteeringOutputs},
12 speed::SpeedOverride,
13};
14
15#[derive(Component, Debug, Copy, Clone, Reflect)]
18#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
19#[cfg_attr(feature = "serialize", serde(default))]
20#[component(on_remove = on_approach_remove)]
21pub struct Approach {
22 pub target_radius: f32,
24 pub target: Vec3,
26 pub slowdown_distance: f32,
29}
30
31impl Default for Approach {
32 fn default() -> Self {
33 Self {
34 target_radius: 0.0,
35 target: Vec3::ZERO,
36 slowdown_distance: 10.0,
37 }
38 }
39}
40
41impl Approach {
42 pub fn new(target: Vec3, target_radius: f32) -> Self {
43 Self {
44 target,
45 target_radius,
46 ..Default::default()
47 }
48 }
49
50 pub fn with_slowdown_distance(mut self, distance: f32) -> Self {
54 self.slowdown_distance = distance;
55 self
56 }
57
58 pub fn set_target(&mut self, target: Vec3) {
59 self.target = target;
60 }
61}
62
63#[derive(QueryData)]
64#[query_data(mutable)]
65pub struct ApproachBehaviorAgentQuery {
66 agent: &'static SteeringAgent,
67 approach: &'static Approach,
68 global_transform: &'static GlobalTransform,
69 forces: Forces,
70 outputs: &'static mut SteeringOutputs,
71 speed_override: &'static mut SpeedOverride,
72}
73
74pub(crate) fn run(mut query: Query<ApproachBehaviorAgentQuery>) {
78 for mut item in query.iter_mut() {
79 let to_target = item.approach.target - item.global_transform.translation();
80 let distance = to_target.length();
81
82 if distance < item.approach.target_radius {
84 item.outputs.clear(BehaviorType::Approach);
85 continue;
86 }
87
88 let speed = (distance / item.approach.slowdown_distance)
90 .sqrt()
91 .clamp(0.0, 1.0);
92
93 let mut steering_target = item.outputs.get(BehaviorType::Approach).unwrap_or_default();
94 steering_target.set_interest(to_target.normalize());
95 item.outputs.set(BehaviorType::Approach, steering_target);
96 item.speed_override.set(speed);
97 }
98}
99
100pub(crate) fn debug_approach(mut gizmos: Gizmos, query: Query<(&GlobalTransform, &Approach)>) {
103 for (transform, approach) in query.iter() {
104 let position = transform.translation();
105 let yellow = Color::srgb(1.0, 1.0, 0.0);
106
107 gizmos.arrow(position, approach.target, yellow);
109
110 if approach.target_radius > 0.0 {
112 gizmos.circle(
113 Isometry3d::from_translation(approach.target),
114 approach.target_radius,
115 yellow,
116 );
117 }
118 }
119}
120
121fn on_approach_remove(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
122 if let Some(mut outputs) = world.get_mut::<SteeringOutputs>(entity) {
123 outputs.clear(BehaviorType::Approach);
124 }
125}