bevy_steering/behaviors/
avoid.rs1use avian3d::prelude::*;
2use bevy::{
3 ecs::{lifecycle::HookContext, query::QueryData, world::DeferredWorld},
4 prelude::*,
5};
6use derivative::Derivative;
7#[cfg(feature = "serialize")]
8use serde::{Deserialize, Serialize};
9
10use crate::{
11 agent::SteeringAgent,
12 control::{BehaviorType, SteeringOutputs, SteeringTarget},
13 prelude::{NearbyObstacles, TrackNearbyObstacles},
14};
15
16#[derive(Component, Debug, Clone, Reflect, Derivative)]
23#[derivative(Default)]
24#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
25#[cfg_attr(feature = "serialize", serde(default))]
26#[require(TrackNearbyObstacles)]
27#[component(on_remove = on_avoid_remove)]
28pub struct Avoid {
29 #[derivative(Default(value = "1.0"))]
31 pub distance: f32,
32}
33
34impl Avoid {
35 pub fn with_distance(mut self, distance: f32) -> Self {
36 self.distance = distance;
37 self
38 }
39}
40
41#[derive(QueryData)]
42#[query_data(mutable)]
43pub struct AvoidBehaviorAgentQuery {
44 entity: Entity,
45 agent: &'static SteeringAgent,
46 avoid: &'static Avoid,
47 velocity: &'static LinearVelocity,
48 global_transform: &'static GlobalTransform,
49 obstacles: &'static NearbyObstacles,
50 track: &'static TrackNearbyObstacles,
51 outputs: &'static mut SteeringOutputs,
52}
53
54pub(crate) fn run(mut query: Query<AvoidBehaviorAgentQuery>) {
55 for mut agent in query.iter_mut() {
56 let mut target = SteeringTarget::default();
57
58 for obstacle in agent.obstacles.values() {
59 if obstacle.distance > agent.avoid.distance {
60 continue;
61 }
62 let Some((impact_normal, _)) = obstacle.impact_normals else {
63 continue;
64 };
65 target.set_danger(-impact_normal);
67 }
68
69 agent.outputs.set(BehaviorType::Avoid, target);
70 }
71}
72
73fn on_avoid_remove(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
74 if let Some(mut outputs) = world.get_mut::<SteeringOutputs>(entity) {
75 outputs.clear(BehaviorType::Avoid);
76 }
77}