avian3d/collision/
collision_events.rs

1//! Collision events for detecting when colliders start or stop touching.
2//!
3//! Depending on your use case, you may want to use either buffered events read
4//! using an [`EventReader`] or observable events triggered for specific entities.
5//! Avian provides both options using separate event types.
6//!
7//! Note that collision events are only sent or triggered for entities that have
8//! the [`CollisionEventsEnabled`] component.
9//!
10//! # Buffered Events
11//!
12//! Avian provides two different buffered collision event types:
13//!
14//! - [`CollisionStarted`]
15//! - [`CollisionEnded`]
16//!
17//! These events are sent when two colliders start or stop touching, and can be read
18//! using an [`EventReader`]. This can be useful for efficiently processing large numbers
19//! of collision events between pairs of entities, such as for detecting bullet hits
20//! or playing impact sounds when two objects collide.
21//!
22//! The events are only sent if one of the entities has the [`CollisionEventsEnabled`] component.
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//! fn print_started_collisions(mut collision_event_reader: EventReader<CollisionStarted>) {
30//!     for CollisionStarted(entity1, entity2) in collision_event_reader.read() {
31//!         println!("{entity1} and {entity2} started colliding");
32//!     }
33//! }
34//! ```
35//!
36//! # Observable Events
37//!
38//! Avian provides two observable collision event types:
39//!
40//! - [`OnCollisionStart`]
41//! - [`OnCollisionEnd`]
42//!
43//! These events are triggered for [observers](Observer) when two colliders start or stop touching.
44//! This makes them good for entity-specific collision scenarios, such as for detecting when a player
45//! steps on a pressure plate or enters a trigger volume.
46//!
47//! The events are only triggered if the target entity has the [`CollisionEventsEnabled`] component.
48//!
49//! ```
50#![cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
51#![cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
52//! use bevy::prelude::*;
53//!
54//! #[derive(Component)]
55//! struct Player;
56//!
57//! #[derive(Component)]
58//! struct PressurePlate;
59//!
60//! fn setup_pressure_plates(mut commands: Commands) {
61//!     commands.spawn((
62//!         PressurePlate,
63#![cfg_attr(feature = "2d", doc = "        Collider::rectangle(1.0, 1.0),")]
64#![cfg_attr(feature = "3d", doc = "        Collider::cuboid(1.0, 0.1, 1.0),")]
65//!         Sensor,
66//!         // Enable collision events for this entity.
67//!         CollisionEventsEnabled,
68//!     ))
69//!     .observe(|trigger: Trigger<OnCollisionStart>, player_query: Query<&Player>| {
70//!         let pressure_plate = trigger.target();
71//!         let other_entity = trigger.collider;
72//!         if player_query.contains(other_entity) {
73//!             println!("Player {other_entity} stepped on pressure plate {pressure_plate}");
74//!         }
75//!     });
76//! }
77//! ```
78
79use bevy::prelude::*;
80
81/// A buffered [collision event](super#collision-events) that is sent when two colliders start touching.
82///
83/// The event is only sent if one of the entities has the [`CollisionEventsEnabled`] component.
84///
85/// Unlike [`OnCollisionStart`], this event is *not* triggered for observers.
86/// Instead, you must use an [`EventReader`] to read the event in a system.
87/// This makes it good for efficiently processing large numbers of collision events
88/// between pairs of entities.
89///
90/// # Example
91///
92/// ```
93#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
94#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
95/// use bevy::prelude::*;
96///
97/// fn print_started_collisions(mut collision_event_reader: EventReader<CollisionStarted>) {
98///     for CollisionStarted(entity1, entity2) in collision_event_reader.read() {
99///         println!("{entity1} and {entity2} started colliding");
100///     }
101/// }
102/// ```
103///
104/// # Scheduling
105///
106/// The [`CollisionStarted`] event is sent in the [`NarrowPhaseSet::Update`] system set,
107/// but can be read at any time.
108///
109/// [`NarrowPhaseSet::Update`]: super::narrow_phase::NarrowPhaseSet::Update
110#[derive(Event, Clone, Copy, Debug, PartialEq)]
111#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
112pub struct CollisionStarted(pub Entity, pub Entity);
113
114/// A buffered [collision event](super#collision-events) that is sent when two colliders stop touching.
115///
116/// The event is only sent if one of the entities has the [`CollisionEventsEnabled`] component.
117///
118/// Unlike [`OnCollisionEnd`], this event is *not* triggered for observers.
119/// Instead, you must use an [`EventReader`] to read the event in a system.
120/// This makes it good for efficiently processing large numbers of collision events
121/// between pairs of entities.
122///
123/// # Example
124///
125/// ```
126#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
127#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
128/// use bevy::prelude::*;
129///
130/// fn print_ended_collisions(mut collision_event_reader: EventReader<CollisionEnded>) {
131///     for CollisionEnded(entity1, entity2) in collision_event_reader.read() {
132///         println!("{entity1} and {entity2} stopped colliding");
133///     }
134/// }
135/// ```
136///
137/// # Scheduling
138///
139/// The [`CollisionEnded`] event is sent in the [`NarrowPhaseSet::Update`] system set,
140/// but can be read at any time.
141///
142/// Note that if one of the colliders was removed or the bounding boxes of the colliders stopped
143/// overlapping, the [`ContactPair`] between the entities was also removed, and the contact data
144/// will not be available through [`Collisions`].
145///
146/// [`NarrowPhaseSet::Update`]: super::narrow_phase::NarrowPhaseSet::Update
147/// [`ContactPair`]: super::ContactPair
148/// [`Collisions`]: super::Collisions
149#[derive(Event, Clone, Copy, Debug, PartialEq)]
150#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
151pub struct CollisionEnded(pub Entity, pub Entity);
152
153/// A [collision event](super#collision-events) that is triggered for [observers](Observer)
154/// when two colliders start touching.
155///
156/// The event is only triggered if the target entity has the [`CollisionEventsEnabled`] component.
157///
158/// Unlike [`CollisionStarted`], this event can *not* be read using an [`EventReader`].
159/// Instead, you must use an [observer](Observer). This makes it good for entity-specific
160/// collision listeners.
161///
162/// # Example
163///
164/// ```
165#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
166#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
167/// use bevy::prelude::*;
168///
169/// #[derive(Component)]
170/// struct Player;
171///
172/// #[derive(Component)]
173/// struct PressurePlate;
174///
175/// fn setup_pressure_plates(mut commands: Commands) {
176///     commands.spawn((
177///         PressurePlate,
178#[cfg_attr(feature = "2d", doc = "        Collider::rectangle(1.0, 1.0),")]
179#[cfg_attr(feature = "3d", doc = "        Collider::cuboid(1.0, 0.1, 1.0),")]
180///         Sensor,
181///         // Enable collision events for this entity.
182///         CollisionEventsEnabled,
183///     ))
184///     .observe(|trigger: Trigger<OnCollisionStart>, player_query: Query<&Player>| {
185///         let pressure_plate = trigger.target();
186///         let other_entity = trigger.collider;
187///         if player_query.contains(other_entity) {
188///             println!("Player {other_entity} stepped on pressure plate {pressure_plate}");
189///         }
190///     });
191/// }
192/// ```
193///
194/// # Scheduling
195///
196/// The [`OnCollisionStart`] event is triggered after the physics step in the [`CollisionEventSystems`]
197/// system set. At this point, the solver has already run and contact impulses have been updated.
198///
199/// [`CollisionEventSystems`]: super::narrow_phase::CollisionEventSystems
200#[derive(Event, Clone, Copy, Debug, PartialEq)]
201#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
202pub struct OnCollisionStart {
203    /// The entity of the collider that started colliding with the [`Trigger::target`].
204    pub collider: Entity,
205    /// The entity of the rigid body that started colliding with the [`Trigger::target`].
206    ///
207    /// If the collider is not attached to a rigid body, this will be `None`.
208    pub body: Option<Entity>,
209}
210
211/// A [collision event](super#collision-events) that is triggered for [observers](Observer)
212/// when two colliders stop touching.
213///
214/// The event is only triggered if the target entity has the [`CollisionEventsEnabled`] component.
215///
216/// Unlike [`CollisionEnded`], this event can *not* be read using an [`EventReader`].
217/// Instead, you must use an [observer](Observer). This makes it good for entity-specific
218/// collision listeners.
219///
220/// # Example
221///
222/// ```
223#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
224#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
225/// use bevy::prelude::*;
226///
227/// #[derive(Component)]
228/// struct Player;
229///
230/// #[derive(Component)]
231/// struct PressurePlate;
232///
233/// fn setup_pressure_plates(mut commands: Commands) {
234///     commands.spawn((
235///         PressurePlate,
236#[cfg_attr(feature = "2d", doc = "        Collider::rectangle(1.0, 1.0),")]
237#[cfg_attr(feature = "3d", doc = "        Collider::cuboid(1.0, 0.1, 1.0),")]
238///         Sensor,
239///         // Enable collision events for this entity.
240///         CollisionEventsEnabled,
241///     ))
242///     .observe(|trigger: Trigger<OnCollisionEnd>, player_query: Query<&Player>| {
243///         let pressure_plate = trigger.target();
244///         let other_entity = trigger.collider;
245///         if player_query.contains(other_entity) {
246///             println!("Player {other_entity} stepped off of pressure plate {pressure_plate}");
247///         }
248///     });
249/// }
250/// ```
251///
252/// # Scheduling
253///
254/// The [`OnCollisionEnd`] event is triggered after the physics step in the [`CollisionEventSystems`]
255/// system set. At this point, the solver has already run and contact impulses have been updated.
256///
257/// Note that if one of the colliders was removed or the bounding boxes of the colliders stopped
258/// overlapping, the [`ContactPair`] between the entities was also removed, and the contact data
259/// will not be available through [`Collisions`].
260///
261/// [`CollisionEventSystems`]: super::narrow_phase::CollisionEventSystems
262/// [`ContactPair`]: super::ContactPair
263/// [`Collisions`]: super::Collisions
264#[derive(Event, Clone, Copy, Debug, PartialEq)]
265#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
266pub struct OnCollisionEnd {
267    /// The entity of the collider that stopped colliding with the [`Trigger::target`].
268    pub collider: Entity,
269    /// The entity of the rigid body that stopped colliding with the [`Trigger::target`].
270    ///
271    /// If the collider is not attached to a rigid body, this will be `None`.
272    pub body: Option<Entity>,
273}
274
275/// A marker component that enables [collision events](self) for an entity.
276///
277/// This enables both the buffered [`CollisionStarted`] and [`CollisionEnded`] events,
278/// as well as the observable [`OnCollisionStart`] and [`OnCollisionEnd`] events.
279#[derive(Component, Clone, Copy, Debug, Default, Reflect)]
280#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
281#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
282#[reflect(Component, Debug)]
283pub struct CollisionEventsEnabled;