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}