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}