bevy_tnua_physics_integration_layer/
obstacle_radar.rs

1use bevy::{platform::collections::HashMap, prelude::*};
2
3use crate::math::{Float, Vector3};
4
5/// Add this to a character entity to detect obstacles around it.
6///
7/// Obstacles can be used for environment movement actions like climbing and wall-jumping.
8///
9/// This component stores rather limited data - only the detected entities and nothing about their
10/// form or position. See `TnuaRadarLens` in the main Tnua crate, which wraps this with a
11/// [`TnuaSpatialExt`](crate::spatial_ext::TnuaSpatialExt) to provide many helper methods for
12/// running more queries on the detected obstacles.
13#[derive(Component)]
14pub struct TnuaObstacleRadar {
15    /// The radius of the radar's cylinder.
16    pub radius: Float,
17    /// The height of the radar's cylinder.
18    pub height: Float,
19    tracked_entity: Entity,
20    tracked_position: Vector3,
21    up_direction: Dir3,
22    blips: HashMap<Entity, BlipStatus>,
23}
24
25impl TnuaObstacleRadar {
26    pub fn new(radius: Float, height: Float) -> Self {
27        Self {
28            radius,
29            height,
30            tracked_entity: Entity::PLACEHOLDER,
31            tracked_position: Vector3::NAN,
32            up_direction: Dir3::Y,
33            blips: Default::default(),
34        }
35    }
36
37    /// Physics integration crates must call this each frame before they start calling
38    /// [`mark_seen`](Self::mark_seen), and feed it some general information about the character
39    /// entity.
40    pub fn pre_marking_update(
41        &mut self,
42        tracked_entity: Entity,
43        tracked_position: Vector3,
44        up_direction: Dir3,
45    ) {
46        self.tracked_entity = tracked_entity;
47        self.tracked_position = tracked_position;
48        self.up_direction = up_direction;
49        self.blips.retain(|_, blip_status| match blip_status {
50            BlipStatus::Unseen => false,
51            BlipStatus::Seen => {
52                *blip_status = BlipStatus::Unseen;
53                true
54            }
55        });
56    }
57
58    /// Physics integration crates should call this for each detected entity during each frame,
59    /// after invoking [`pre_marking_update`](Self::pre_marking_update).
60    pub fn mark_seen(&mut self, entity: Entity) {
61        self.blips.insert(entity, BlipStatus::Seen);
62    }
63
64    /// Get the character entity who owns the radar (not the detected obstacle entities!)
65    pub fn tracked_entity(&self) -> Entity {
66        self.tracked_entity
67    }
68
69    /// Get the position of the character who owns the radar (not the detected obstacle's
70    /// positions!)
71    pub fn tracked_position(&self) -> Vector3 {
72        self.tracked_position
73    }
74
75    /// Get the direction considered up.
76    pub fn up_direction(&self) -> Dir3 {
77        self.up_direction
78    }
79
80    /// Iterate over all the blip entities.
81    pub fn iter_blips(&self) -> impl '_ + Iterator<Item = Entity> {
82        self.blips.keys().copied()
83    }
84
85    /// Check if a particular entity has been detected this frame.
86    pub fn has_blip(&self, entity: Entity) -> bool {
87        self.blips.contains_key(&entity)
88    }
89}
90
91pub enum BlipStatus {
92    Unseen,
93    Seen,
94}