bevy_tnua/
sensor_sets.rs

1use crate::ghost_overrides::TnuaGhostOverwritesForBasis;
2use crate::{TnuaBasis, TnuaScheme, math::*};
3use bevy::prelude::*;
4use bevy_tnua_physics_integration_layer::data_for_backends::{
5    TnuaGhostSensor, TnuaProximitySensor, TnuaSensorOf,
6};
7
8/// The entities that hold the [proximity sensors](TnuaProximitySensor) of the basis.
9///
10/// Automatically added for [`TnuaController`](crate::TnuaController).
11#[derive(Component, Deref)]
12pub struct TnuaSensorsEntities<S: TnuaScheme> {
13    pub(crate) sensors_entities:
14        <<S::Basis as TnuaBasis>::Sensors<'static> as TnuaSensors<'static>>::Entities,
15}
16
17pub trait TnuaSensors<'a>: 'a + Copy + Clone {
18    type Entities: 'static + Send + Sync + Default;
19    #[cfg(feature = "serialize")]
20    type GhostOverwrites: TnuaGhostOverwritesForBasis<Entities = Self::Entities>
21        + serde::Serialize
22        + for<'de> serde::Deserialize<'de>;
23    #[cfg(not(feature = "serialize"))]
24    type GhostOverwrites: TnuaGhostOverwritesForBasis<Entities = Self::Entities>;
25}
26
27pub struct ProximitySensorPreparationHelper {
28    pub cast_origin: Vector3,
29    pub cast_direction: Dir3,
30    pub cast_shape_rotation: Quaternion,
31    pub cast_range: Float,
32    pub ghost_sensor: bool,
33}
34
35impl Default for ProximitySensorPreparationHelper {
36    fn default() -> Self {
37        Self {
38            cast_origin: Vector3::ZERO,
39            cast_direction: Dir3::NEG_Y,
40            cast_shape_rotation: Quaternion::IDENTITY,
41            cast_range: 0.0,
42            ghost_sensor: false,
43        }
44    }
45}
46
47impl ProximitySensorPreparationHelper {
48    fn already_set_in_sensor(&self, sensor: &TnuaProximitySensor) -> bool {
49        let Self {
50            cast_origin,
51            cast_direction,
52            cast_shape_rotation,
53            cast_range,
54            ghost_sensor: _,
55        } = self;
56        *cast_origin == sensor.cast_origin
57            && *cast_direction == sensor.cast_direction
58            && *cast_shape_rotation == sensor.cast_shape_rotation
59            && *cast_range == sensor.cast_range
60    }
61
62    fn to_sensor(&self) -> TnuaProximitySensor {
63        TnuaProximitySensor {
64            cast_origin: self.cast_origin,
65            cast_direction: self.cast_direction,
66            cast_shape_rotation: self.cast_shape_rotation,
67            cast_range: self.cast_range,
68            output: None,
69        }
70    }
71
72    pub fn prepare_for<'a>(
73        &self,
74        put_in_entity: &mut Option<Entity>,
75        proximity_sensors_query: &'a Query<(&TnuaProximitySensor, Has<TnuaGhostSensor>)>,
76        controller_entity: Entity,
77        commands: &mut Commands,
78    ) -> Option<&'a TnuaProximitySensor> {
79        if let Some(sensor_entity) = put_in_entity
80            && let Ok((existing_sensor, has_ghost_sensor)) =
81                proximity_sensors_query.get(*sensor_entity)
82        {
83            if !self.already_set_in_sensor(existing_sensor) {
84                // TODO: send a command that only alters the sensor properties?
85                commands.entity(*sensor_entity).insert(self.to_sensor());
86            }
87            if self.ghost_sensor && !has_ghost_sensor {
88                commands
89                    .entity(*sensor_entity)
90                    .insert(TnuaGhostSensor::default());
91            } else if !self.ghost_sensor && has_ghost_sensor {
92                commands
93                    .entity(*sensor_entity)
94                    .try_remove::<TnuaGhostSensor>();
95            }
96            Some(existing_sensor)
97        } else {
98            commands
99                .entity(controller_entity)
100                .with_related_entities::<TnuaSensorOf>(|commands| {
101                    let mut cmd = commands.spawn(self.to_sensor());
102                    if self.ghost_sensor {
103                        cmd.insert(TnuaGhostSensor::default());
104                    }
105                    *put_in_entity = Some(cmd.id());
106                });
107            None
108        }
109    }
110
111    pub fn ensure_not_existing<'a>(
112        put_in_entity: &mut Option<Entity>,
113        proximity_sensors_query: &'a Query<(&TnuaProximitySensor, Has<TnuaGhostSensor>)>,
114        commands: &mut Commands,
115    ) -> Option<&'a TnuaProximitySensor> {
116        if let Some(sensor_entity) = put_in_entity {
117            if proximity_sensors_query.contains(*sensor_entity) {
118                commands.entity(*sensor_entity).despawn();
119            } else {
120                *put_in_entity = None;
121            }
122        }
123        None
124    }
125}