rapier2d/pipeline/event_handler.rs
1#[cfg(feature = "alloc")]
2use crate::dynamics::{RigidBodySet, SoftBodySet, SoftBodyTearEvent};
3#[cfg(all(feature = "std", feature = "alloc"))]
4use crate::geometry::ContactForceEvent;
5#[cfg(feature = "alloc")]
6use crate::geometry::{ColliderSet, CollisionEvent, ContactPair};
7#[cfg(feature = "alloc")]
8use crate::math::Real;
9
10bitflags::bitflags! {
11 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
12 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
13 /// Flags that control which physics events are generated for a collider.
14 ///
15 /// By default, colliders don't generate events (for performance). Enable specific events
16 /// per-collider using these flags.
17 ///
18 /// # Example
19 /// ```
20 /// # use rapier3d::prelude::*;
21 /// // Enable collision start/stop events for a trigger zone
22 /// let trigger = ColliderBuilder::cuboid(5.0, 5.0, 5.0)
23 /// .sensor(true)
24 /// .active_events(ActiveEvents::COLLISION_EVENTS)
25 /// .build();
26 ///
27 /// // Enable force events for breakable glass
28 /// let glass = ColliderBuilder::cuboid(1.0, 2.0, 0.1)
29 /// .active_events(ActiveEvents::CONTACT_FORCE_EVENTS)
30 /// .contact_force_event_threshold(1000.0)
31 /// .build();
32 /// ```
33 pub struct ActiveEvents: u32 {
34 /// Enables `Started`/`Stopped` collision events for this collider.
35 ///
36 /// You'll receive events when this collider starts or stops touching others.
37 const COLLISION_EVENTS = 0b0001;
38
39 /// Enables contact force events when forces exceed a threshold.
40 ///
41 /// You'll receive events when contact forces surpass `contact_force_event_threshold`.
42 const CONTACT_FORCE_EVENTS = 0b0010;
43 }
44}
45
46impl Default for ActiveEvents {
47 fn default() -> Self {
48 ActiveEvents::empty()
49 }
50}
51
52/// A callback interface for receiving physics events (collisions starting/stopping, contact forces,
53/// soft-body tears).
54///
55/// Implement this trait to get notified when:
56/// - Two colliders start or stop touching ([`handle_collision_event`](Self::handle_collision_event))
57/// - Contact forces exceed a threshold ([`handle_contact_force_event`](Self::handle_contact_force_event))
58/// - A soft body tears ([`handle_soft_body_tear_event`](Self::handle_soft_body_tear_event))
59///
60/// # Common use cases
61/// - Playing sound effects when objects collide
62/// - Triggering game events (damage, pickups, checkpoints)
63/// - Monitoring structural stress
64/// - Detecting when specific objects touch
65///
66/// # Built-in implementation
67/// Use [`ChannelEventCollector`] to collect events into channels for processing after the physics step.
68///
69/// # Example
70/// ```
71/// # use rapier3d::prelude::*;
72/// # use rapier3d::geometry::ContactPair;
73/// struct MyEventHandler;
74///
75/// impl EventHandler for MyEventHandler {
76/// fn handle_collision_event(
77/// &self,
78/// bodies: &RigidBodySet,
79/// colliders: &ColliderSet,
80/// event: CollisionEvent,
81/// contact_pair: Option<&ContactPair>,
82/// ) {
83/// match event {
84/// CollisionEvent::Started(h1, h2, _) => {
85/// println!("Collision started between {:?} and {:?}", h1, h2);
86/// }
87/// CollisionEvent::Stopped(h1, h2, _) => {
88/// println!("Collision ended between {:?} and {:?}", h1, h2);
89/// }
90/// }
91/// }
92/// # fn handle_contact_force_event(&self, _dt: Real, _bodies: &RigidBodySet, _colliders: &ColliderSet, _contact_pair: &ContactPair, _total_force_magnitude: Real) {}
93/// # fn handle_soft_body_tear_event(&self, _soft_bodies: &SoftBodySet, _event: &SoftBodyTearEvent) {}
94/// }
95/// ```
96#[cfg(feature = "alloc")]
97pub trait EventHandler: crate::utils::MaybeSync {
98 /// Called when two colliders start or stop touching each other.
99 ///
100 /// Collision events are triggered when intersection state changes (Started/Stopped).
101 /// At least one collider must have [`ActiveEvents::COLLISION_EVENTS`] enabled.
102 ///
103 /// # Parameters
104 /// * `event` - Either `Started(h1, h2, flags)` or `Stopped(h1, h2, flags)`
105 /// * `bodies` - All rigid bodies (to look up body info)
106 /// * `colliders` - All colliders (to look up collider info)
107 /// * `contact_pair` - Detailed contact info (`None` for sensors, since they don't compute contacts)
108 ///
109 /// # Use cases
110 /// - Play collision sound effects
111 /// - Apply damage when objects hit
112 /// - Trigger game events (entering zones, picking up items)
113 /// - Track what's touching what
114 fn handle_collision_event(
115 &self,
116 bodies: &RigidBodySet,
117 colliders: &ColliderSet,
118 event: CollisionEvent,
119 contact_pair: Option<&ContactPair>,
120 );
121
122 /// Called when contact forces exceed a threshold.
123 ///
124 /// Triggered when the total force magnitude between two colliders exceeds the
125 /// [`Collider::contact_force_event_threshold`](crate::geometry::Collider::set_contact_force_event_threshold).
126 /// At least one collider must have [`ActiveEvents::CONTACT_FORCE_EVENTS`] enabled.
127 ///
128 /// # Use cases
129 /// - Detect hard impacts (for damage, breaking objects)
130 /// - Monitor structural stress
131 /// - Trigger effects at certain force levels (sparks, cracks)
132 ///
133 /// # Parameters
134 /// * `total_force_magnitude` - Sum of magnitudes of all contact forces (not vector sum!)
135 /// Example: Two forces `[0, 100, 0]` and `[0, -100, 0]` → magnitude = 200 (not 0)
136 fn handle_contact_force_event(
137 &self,
138 dt: Real,
139 bodies: &RigidBodySet,
140 colliders: &ColliderSet,
141 contact_pair: &ContactPair,
142 total_force_magnitude: Real,
143 );
144
145 /// Called at the end of a step for every soft body that tore during it (an element past its
146 /// tear threshold, or a `SoftBody::tear_edge`/`tear_cell` request), once the topology change is
147 /// applied ([`SoftBodyTearEvent`]); immediate `SoftBodySet::tear`/`cut` return theirs instead.
148 fn handle_soft_body_tear_event(&self, soft_bodies: &SoftBodySet, event: &SoftBodyTearEvent);
149}
150
151#[cfg(feature = "alloc")]
152impl EventHandler for () {
153 fn handle_collision_event(
154 &self,
155 _bodies: &RigidBodySet,
156 _colliders: &ColliderSet,
157 _event: CollisionEvent,
158 _contact_pair: Option<&ContactPair>,
159 ) {
160 }
161
162 fn handle_contact_force_event(
163 &self,
164 _dt: Real,
165 _bodies: &RigidBodySet,
166 _colliders: &ColliderSet,
167 _contact_pair: &ContactPair,
168 _total_force_magnitude: Real,
169 ) {
170 }
171
172 fn handle_soft_body_tear_event(&self, _soft_bodies: &SoftBodySet, _event: &SoftBodyTearEvent) {}
173}
174
175/// A ready-to-use event handler that collects events into channels for later processing.
176///
177/// Instead of processing events immediately during physics step, this collector sends them
178/// to channels that you can poll from your game loop. This is the recommended approach.
179///
180/// # Example
181/// ```
182/// # use rapier3d::prelude::*;
183/// use std::sync::mpsc::channel;
184///
185/// let (collision_send, collision_recv) = channel();
186/// let (contact_force_send, contact_force_recv) = channel();
187/// let (soft_body_tear_send, soft_body_tear_recv) = channel();
188/// let event_handler =
189/// ChannelEventCollector::new(collision_send, contact_force_send, soft_body_tear_send);
190///
191/// // After physics step:
192/// while let Ok(collision_event) = collision_recv.try_recv() {
193/// match collision_event {
194/// CollisionEvent::Started(h1, h2, _) => println!("Collision!"),
195/// CollisionEvent::Stopped(h1, h2, _) => println!("Separated"),
196/// }
197/// }
198/// while let Ok(tear_event) = soft_body_tear_recv.try_recv() {
199/// println!("Soft body {:?} tore", tear_event.soft_body);
200/// }
201/// ```
202#[cfg(feature = "std")]
203pub struct ChannelEventCollector {
204 collision_event_sender: std::sync::mpsc::Sender<CollisionEvent>,
205 contact_force_event_sender: std::sync::mpsc::Sender<ContactForceEvent>,
206 soft_body_tear_event_sender: std::sync::mpsc::Sender<SoftBodyTearEvent>,
207}
208
209#[cfg(feature = "std")]
210impl ChannelEventCollector {
211 /// Initialize a new event collector from channel senders, one per event kind.
212 ///
213 /// A sender whose receiver was dropped just discards the events of its kind.
214 pub fn new(
215 collision_event_sender: std::sync::mpsc::Sender<CollisionEvent>,
216 contact_force_event_sender: std::sync::mpsc::Sender<ContactForceEvent>,
217 soft_body_tear_event_sender: std::sync::mpsc::Sender<SoftBodyTearEvent>,
218 ) -> Self {
219 Self {
220 collision_event_sender,
221 contact_force_event_sender,
222 soft_body_tear_event_sender,
223 }
224 }
225}
226
227#[cfg(feature = "std")]
228impl EventHandler for ChannelEventCollector {
229 fn handle_collision_event(
230 &self,
231 _bodies: &RigidBodySet,
232 _colliders: &ColliderSet,
233 event: CollisionEvent,
234 _: Option<&ContactPair>,
235 ) {
236 let _ = self.collision_event_sender.send(event);
237 }
238
239 fn handle_contact_force_event(
240 &self,
241 dt: Real,
242 _bodies: &RigidBodySet,
243 _colliders: &ColliderSet,
244 contact_pair: &ContactPair,
245 total_force_magnitude: Real,
246 ) {
247 let result = ContactForceEvent::from_contact_pair(dt, contact_pair, total_force_magnitude);
248 let _ = self.contact_force_event_sender.send(result);
249 }
250
251 fn handle_soft_body_tear_event(&self, _soft_bodies: &SoftBodySet, event: &SoftBodyTearEvent) {
252 let _ = self.soft_body_tear_event_sender.send(event.clone());
253 }
254}