avian2d/collision/collision_events.rs
1//! Collision events for detecting when colliders start or stop touching.
2//!
3//! Avian provides two collision event types:
4//!
5//! - [`CollisionStart`]: Triggered when two colliders start touching.
6//! - [`CollisionEnd`]: Triggered when two colliders stop touching.
7//!
8//! Depending on your use case, you may want to read them as [`Message`]s with a [`MessageReader`],
9//! or observe them as [`Event`]s with an [observer](Observer). Avian supports both options.
10//!
11//! # Reading Collision Events
12//!
13//! To enable collision events for a collider entity, add the [`CollisionEventsEnabled`] component.
14//!
15//! ```
16#![cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
17#![cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
18//! use bevy::prelude::*;
19//!
20//! #[derive(Component)]
21//! struct PressurePlate;
22//!
23//! fn setup_pressure_plates(mut commands: Commands) {
24//! commands.spawn((
25//! PressurePlate,
26#![cfg_attr(feature = "2d", doc = " Collider::rectangle(1.0, 1.0),")]
27#![cfg_attr(feature = "3d", doc = " Collider::cuboid(1.0, 0.1, 1.0),")]
28//! Sensor,
29//! // Enable collision events for this entity.
30//! CollisionEventsEnabled,
31//! ));
32//! }
33//! ```
34//!
35//! The [`CollisionStart`] and [`CollisionEnd`] events will now be both written as a [`Message`]
36//! and triggered as an [`Event`] when the entity starts or stops touching another collider.
37//! It is up to you to decide which event processing method is best suited for your use case.
38//!
39//! ## Collision `Message`
40//!
41//! Reading collision events as [`Message`]s with a [`MessageReader`] can be very efficient
42//! for processing large numbers of collisions between pairs of entities, such as for detecting
43//! bullet hits or playing impact sounds when two objects collide.
44//!
45//! The events are only written if one of the entities has the [`CollisionEventsEnabled`] component.
46//!
47//! ```
48#![cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
49#![cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
50//! # use bevy::prelude::*;
51//! #
52//! fn print_started_collisions(mut collision_reader: MessageReader<CollisionStart>) {
53//! for event in collision_reader.read() {
54//! println!("{} and {} started colliding", event.collider1, event.collider2);
55//! }
56//! }
57//! ```
58//!
59//! ## Collision `Event`
60//!
61//! Observing collision events as [`Event`]s with an [observer](Observer) can be very useful
62//! for entity-specific collision scenarios, such as for detecting when a player steps on
63//! a pressure plate or enters a trigger volume.
64//!
65//! The events are only triggered if the target entity has the [`CollisionEventsEnabled`] component.
66//!
67//! ```
68#![cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
69#![cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
70//! # use bevy::prelude::*;
71//! #
72//! # #[derive(Component)]
73//! # struct Player;
74//! #
75//! # #[derive(Component)]
76//! # struct PressurePlate;
77//! #
78//! fn setup_pressure_plates(mut commands: Commands) {
79//! commands.spawn((
80//! PressurePlate,
81#![cfg_attr(feature = "2d", doc = " Collider::rectangle(1.0, 1.0),")]
82#![cfg_attr(feature = "3d", doc = " Collider::cuboid(1.0, 0.1, 1.0),")]
83//! Sensor,
84//! // Enable collision events for this entity.
85//! CollisionEventsEnabled,
86//! ))
87//! .observe(on_player_stepped_on_plate);
88//! }
89//!
90//! fn on_player_stepped_on_plate(event: On<CollisionStart>, player_query: Query<&Player>) {
91//! // `colider1` and `body1` refer to the event target and its body.
92//! // `collider2` and `body2` refer to the other collider and its body.
93//! let pressure_plate = event.collider1;
94//! let other_entity = event.collider2;
95//!
96//! if player_query.contains(other_entity) {
97//! println!("Player {other_entity} stepped on pressure plate {pressure_plate}");
98//! }
99//! }
100//! ```
101
102use bevy::prelude::*;
103
104/// A [collision event](self) that is triggered when two colliders start touching.
105///
106/// The event can be read using a [`MessageReader`] or observed using an [observer](Observer).
107/// It is only triggered for entities with the [`CollisionEventsEnabled`] component.
108///
109/// # Example
110///
111/// Below is an example of observing the [`CollisionStart`] event using an [observer](Observer).
112///
113/// ```
114#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
115#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
116/// use bevy::prelude::*;
117///
118/// #[derive(Component)]
119/// struct Player;
120///
121/// #[derive(Component)]
122/// struct PressurePlate;
123///
124/// fn setup_pressure_plates(mut commands: Commands) {
125/// commands.spawn((
126/// PressurePlate,
127#[cfg_attr(feature = "2d", doc = " Collider::rectangle(1.0, 1.0),")]
128#[cfg_attr(feature = "3d", doc = " Collider::cuboid(1.0, 0.1, 1.0),")]
129/// Sensor,
130/// // Enable collision events for this entity.
131/// CollisionEventsEnabled,
132/// ))
133/// .observe(on_player_stepped_on_plate);
134/// }
135///
136/// fn on_player_stepped_on_plate(event: On<CollisionStart>, player_query: Query<&Player>) {
137/// // `colider1` and `body1` refer to the event target and its body.
138/// // `collider2` and `body2` refer to the other collider and its body.
139/// let pressure_plate = event.collider1;
140/// let other_entity = event.collider2;
141///
142/// if player_query.contains(other_entity) {
143/// println!("Player {other_entity} stepped on pressure plate {pressure_plate}");
144/// }
145/// }
146/// ```
147///
148/// The event can also be read as a [`Message`] using a [`MessageReader`].
149/// This can be more efficient for processing large numbers of collisions.
150///
151/// ```
152#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
153#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
154/// # use bevy::prelude::*;
155/// #
156/// fn print_started_collisions(mut collision_reader: MessageReader<CollisionStart>) {
157/// for event in collision_reader.read() {
158/// println!("{} and {} started colliding", event.collider1, event.collider2);
159/// }
160/// }
161/// ```
162///
163/// # Scheduling
164///
165/// The [`CollisionStart`] event is triggered after the physics step in the [`CollisionEventSystems`]
166/// system set. At this point, the solver has already run and contact impulses have been updated.
167///
168/// [`CollisionEventSystems`]: super::narrow_phase::CollisionEventSystems
169#[derive(EntityEvent, Message, Clone, Copy, Debug, PartialEq)]
170#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
171pub struct CollisionStart {
172 /// The entity of the collider that started colliding with [`collider2`](Self::collider2).
173 ///
174 /// For observers watching this event as an [`EntityEvent`], this is the target entity.
175 #[event_target]
176 pub collider1: Entity,
177 /// The entity of the collider that started colliding with [`collider1`](Self::collider1).
178 pub collider2: Entity,
179 /// The rigid body that [`collider1`](Self::collider1) is attached to.
180 ///
181 /// If the collider is not attached to a rigid body, this will be `None`.
182 pub body1: Option<Entity>,
183 /// The rigid body that [`collider2`](Self::collider2) is attached to.
184 ///
185 /// If the collider is not attached to a rigid body, this will be `None`.
186 pub body2: Option<Entity>,
187 // TODO: Flags to expose the reason for the event, among other things.
188 // flags: CollisionStartFlags,
189}
190
191/// A deprecated alias for [`CollisionStart`].
192#[deprecated(since = "0.4.0", note = "Renamed to `CollisionStart`")]
193pub type OnCollisionStart = CollisionStart;
194
195/// A [collision event](self) that is triggered when two colliders stop touching.
196///
197/// The event can be read using a [`MessageReader`] or observed using an [observer](Observer).
198/// It is only triggered for entities with the [`CollisionEventsEnabled`] component.
199///
200/// # Example
201///
202/// Below is an example of observing the [`CollisionEnd`] event using an [observer](Observer).
203///
204/// ```
205#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
206#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
207/// use bevy::prelude::*;
208///
209/// #[derive(Component)]
210/// struct Player;
211///
212/// #[derive(Component)]
213/// struct PressurePlate;
214///
215/// fn setup_pressure_plates(mut commands: Commands) {
216/// commands.spawn((
217/// PressurePlate,
218#[cfg_attr(feature = "2d", doc = " Collider::rectangle(1.0, 1.0),")]
219#[cfg_attr(feature = "3d", doc = " Collider::cuboid(1.0, 0.1, 1.0),")]
220/// Sensor,
221/// // Enable collision events for this entity.
222/// CollisionEventsEnabled,
223/// ))
224/// .observe(on_player_stepped_off_plate);
225/// }
226///
227/// fn on_player_stepped_off_plate(event: On<CollisionEnd>, player_query: Query<&Player>) {
228/// // `colider1` and `body1` refer to the event target and its body.
229/// // `collider2` and `body2` refer to the other collider and its body.
230/// let pressure_plate = event.collider1;
231/// let other_entity = event.collider2;
232///
233/// if player_query.contains(other_entity) {
234/// println!("Player {other_entity} stepped off pressure plate {pressure_plate}");
235/// }
236/// }
237/// ```
238///
239/// The event can also be read as a [`Message`] using a [`MessageReader`].
240/// This can be more efficient for processing large numbers of collisions.
241///
242/// ```
243#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
244#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
245/// # use bevy::prelude::*;
246/// #
247/// fn print_ended_collisions(mut collision_reader: MessageReader<CollisionEnd>) {
248/// for event in collision_reader.read() {
249/// println!("{} and {} stopped colliding", event.collider1, event.collider2);
250/// }
251/// }
252/// ```
253///
254/// # Scheduling
255///
256/// The [`CollisionEnd`] event is triggered after the physics step in the [`CollisionEventSystems`]
257/// system set. At this point, the solver has already run and contact impulses have been updated.
258///
259/// Note that if one of the colliders was removed or the bounding boxes of the colliders stopped
260/// overlapping, the [`ContactPair`] between the entities was also removed, and the contact data
261/// will not be available through [`Collisions`].
262///
263/// [`CollisionEventSystems`]: super::narrow_phase::CollisionEventSystems
264/// [`ContactPair`]: super::ContactPair
265/// [`Collisions`]: super::Collisions
266#[derive(EntityEvent, Message, Clone, Copy, Debug, PartialEq)]
267#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
268pub struct CollisionEnd {
269 /// The entity of the collider that stopped colliding with [`collider2`](Self::collider2).
270 ///
271 /// For observers watching this event as an [`EntityEvent`], this is the target entity.
272 #[event_target]
273 pub collider1: Entity,
274 /// The entity of the collider that stopped colliding with [`collider1`](Self::collider1).
275 pub collider2: Entity,
276 /// The rigid body that [`collider1`](Self::collider1) is attached to.
277 ///
278 /// If the collider is not attached to a rigid body, this will be `None`.
279 pub body1: Option<Entity>,
280 /// The rigid body that [`collider2`](Self::collider2) is attached to.
281 ///
282 /// If the collider is not attached to a rigid body, this will be `None`.
283 pub body2: Option<Entity>,
284 // TODO: Flags to expose the reason for the event, among other things.
285 // flags: CollisionEndFlags,
286}
287
288/// A deprecated alias for [`CollisionEnd`].
289#[deprecated(since = "0.4.0", note = "Renamed to `CollisionEnd`")]
290pub type OnCollisionEnd = CollisionEnd;
291
292/// A marker component that enables [collision events](self) for an entity.
293#[derive(Component, Clone, Copy, Debug, Default, Reflect)]
294#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
295#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
296#[reflect(Component, Debug)]
297pub struct CollisionEventsEnabled;