avian2d/collision/contact_types/
system_param.rs

1use crate::data_structures::pair_key::PairKey;
2use bevy::{ecs::system::SystemParam, prelude::*};
3
4use super::{ContactGraph, ContactPair};
5
6/// A [`SystemParam`] for accessing and querying collision data.
7///
8/// This is a wrapper around the [`ContactGraph`] resource that provides a convenient API
9/// for querying touching [`ContactPair`]s between entities. If you need more lower-level control
10/// and access to non-touching contact pairs, consider using the [`ContactGraph`] directly.
11///
12/// # Usage
13///
14/// The following methods can be used for querying collisions:
15///
16/// - [`get`](Self::get)
17/// - [`iter`](Self::iter)
18/// - [`contains`](Self::contains)
19/// - [`collisions_with`](Self::collisions_with)
20/// - [`entities_colliding_with`](Self::entities_colliding_with)
21///
22/// For example, to iterate over all collisions with a given entity:
23///
24/// ```
25#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
26#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
27/// use bevy::prelude::*;
28///
29/// #[derive(Component)]
30/// struct PressurePlate;
31///
32/// fn activate_pressure_plates(mut query: Query<Entity, With<PressurePlate>>, collisions: Collisions) {
33///     for pressure_plate in &query {
34///         // Compute the total impulse applied to the pressure plate.
35///         let mut total_impulse = 0.0;
36///
37///         for contact_pair in collisions.collisions_with(pressure_plate) {
38///             total_impulse += contact_pair.total_normal_impulse_magnitude();
39///         }
40///
41///         if total_impulse > 5.0 {
42///             println!("Pressure plate activated!");
43///         }
44///     }
45/// }
46/// ```
47///
48/// Contact modification and filtering should be done using [`CollisionHooks`].
49/// See the documentation for more information.
50///
51/// [`CollisionHooks`]: crate::collision::hooks::CollisionHooks
52#[derive(SystemParam)]
53pub struct Collisions<'w> {
54    /// The [`ContactGraph`] that stores all contact edges.
55    contact_graph: ResMut<'w, ContactGraph>,
56}
57
58impl Collisions<'_> {
59    /// Returns a reference to the internal [`ContactGraph`].
60    ///
61    /// Note that unlike [`Collisions`], which only provides touching contacts,
62    /// the contact graph includes both touching and non-touching contacts.
63    #[inline]
64    pub fn graph(&self) -> &ContactGraph {
65        &self.contact_graph
66    }
67
68    /// Returns a touching contact pair between two entities.
69    /// If the pair does not exist, `None` is returned.
70    #[inline]
71    pub fn get(&self, entity1: Entity, entity2: Entity) -> Option<&ContactPair> {
72        self.contact_graph
73            .get(entity1, entity2)
74            .map(|(_edge, pair)| pair)
75    }
76
77    /// Returns `true` if the given entities have a touching contact pair.
78    #[inline]
79    pub fn contains(&self, entity1: Entity, entity2: Entity) -> bool {
80        self.contact_graph.contains(entity1, entity2)
81    }
82
83    /// Returns `true` if the given pair key matches a touching contact pair.
84    ///
85    /// The pair key should be equivalent to `PairKey::new(entity1.index(), entity2.index())`.
86    ///
87    /// This method can be useful to avoid constructing a new `PairKey` when the key is already known.
88    /// If the key is not available, consider using [`contains`](Self::contains) instead.
89    #[inline]
90    pub fn contains_key(&self, pair_key: &PairKey) -> bool {
91        self.contact_graph.contains_key(pair_key)
92    }
93
94    /// Returns an iterator yielding immutable access to all touching contact pairs.
95    #[inline]
96    pub fn iter(&self) -> impl Iterator<Item = &ContactPair> {
97        self.contact_graph
98            .iter_active_touching()
99            .chain(self.contact_graph.iter_sleeping_touching())
100    }
101
102    /// Returns an iterator yielding immutable access to all touching contact pairs
103    /// involving the given entity.
104    #[inline]
105    pub fn collisions_with(&self, entity: Entity) -> impl Iterator<Item = &ContactPair> {
106        self.contact_graph
107            .contact_pairs_with(entity)
108            .filter(|contact_pair| contact_pair.is_touching())
109    }
110
111    /// Returns an iterator yielding immutable access to all entities
112    /// that have a touching contact pair with the given entity.
113    #[inline]
114    pub fn entities_colliding_with(&self, entity: Entity) -> impl Iterator<Item = Entity> + '_ {
115        // TODO: Can we do this more efficiently?
116        self.collisions_with(entity).map(move |contacts| {
117            if contacts.collider1 == entity {
118                contacts.collider2
119            } else {
120                contacts.collider1
121            }
122        })
123    }
124}