rapier2d/pipeline/event_handler.rs
1#[cfg(feature = "alloc")]
2use crate::dynamics::RigidBodySet;
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///
54/// Implement this trait to get notified when:
55/// - Two colliders start or stop touching ([`handle_collision_event`](Self::handle_collision_event))
56/// - Contact forces exceed a threshold ([`handle_contact_force_event`](Self::handle_contact_force_event))
57///
58/// # Common use cases
59/// - Playing sound effects when objects collide
60/// - Triggering game events (damage, pickups, checkpoints)
61/// - Monitoring structural stress
62/// - Detecting when specific objects touch
63///
64/// # Built-in implementation
65/// Use [`ChannelEventCollector`] to collect events into channels for processing after the physics step.
66///
67/// # Example
68/// ```
69/// # use rapier3d::prelude::*;
70/// # use rapier3d::geometry::ContactPair;
71/// struct MyEventHandler;
72///
73/// impl EventHandler for MyEventHandler {
74/// fn handle_collision_event(
75/// &self,
76/// bodies: &RigidBodySet,
77/// colliders: &ColliderSet,
78/// event: CollisionEvent,
79/// contact_pair: Option<&ContactPair>,
80/// ) {
81/// match event {
82/// CollisionEvent::Started(h1, h2, _) => {
83/// println!("Collision started between {:?} and {:?}", h1, h2);
84/// }
85/// CollisionEvent::Stopped(h1, h2, _) => {
86/// println!("Collision ended between {:?} and {:?}", h1, h2);
87/// }
88/// }
89/// }
90/// # fn handle_contact_force_event(&self, _dt: Real, _bodies: &RigidBodySet, _colliders: &ColliderSet, _contact_pair: &ContactPair, _total_force_magnitude: Real) {}
91/// }
92/// ```
93#[cfg(feature = "alloc")]
94pub trait EventHandler: crate::utils::MaybeSync {
95 /// Called when two colliders start or stop touching each other.
96 ///
97 /// Collision events are triggered when intersection state changes (Started/Stopped).
98 /// At least one collider must have [`ActiveEvents::COLLISION_EVENTS`] enabled.
99 ///
100 /// # Parameters
101 /// * `event` - Either `Started(h1, h2, flags)` or `Stopped(h1, h2, flags)`
102 /// * `bodies` - All rigid bodies (to look up body info)
103 /// * `colliders` - All colliders (to look up collider info)
104 /// * `contact_pair` - Detailed contact info (`None` for sensors, since they don't compute contacts)
105 ///
106 /// # Use cases
107 /// - Play collision sound effects
108 /// - Apply damage when objects hit
109 /// - Trigger game events (entering zones, picking up items)
110 /// - Track what's touching what
111 fn handle_collision_event(
112 &self,
113 bodies: &RigidBodySet,
114 colliders: &ColliderSet,
115 event: CollisionEvent,
116 contact_pair: Option<&ContactPair>,
117 );
118
119 /// Called when contact forces exceed a threshold.
120 ///
121 /// Triggered when the total force magnitude between two colliders exceeds the
122 /// [`Collider::contact_force_event_threshold`](crate::geometry::Collider::set_contact_force_event_threshold).
123 /// At least one collider must have [`ActiveEvents::CONTACT_FORCE_EVENTS`] enabled.
124 ///
125 /// # Use cases
126 /// - Detect hard impacts (for damage, breaking objects)
127 /// - Monitor structural stress
128 /// - Trigger effects at certain force levels (sparks, cracks)
129 ///
130 /// # Parameters
131 /// * `total_force_magnitude` - Sum of magnitudes of all contact forces (not vector sum!)
132 /// Example: Two forces `[0, 100, 0]` and `[0, -100, 0]` → magnitude = 200 (not 0)
133 fn handle_contact_force_event(
134 &self,
135 dt: Real,
136 bodies: &RigidBodySet,
137 colliders: &ColliderSet,
138 contact_pair: &ContactPair,
139 total_force_magnitude: Real,
140 );
141}
142
143#[cfg(feature = "alloc")]
144impl EventHandler for () {
145 fn handle_collision_event(
146 &self,
147 _bodies: &RigidBodySet,
148 _colliders: &ColliderSet,
149 _event: CollisionEvent,
150 _contact_pair: Option<&ContactPair>,
151 ) {
152 }
153
154 fn handle_contact_force_event(
155 &self,
156 _dt: Real,
157 _bodies: &RigidBodySet,
158 _colliders: &ColliderSet,
159 _contact_pair: &ContactPair,
160 _total_force_magnitude: Real,
161 ) {
162 }
163}
164
165/// A ready-to-use event handler that collects events into channels for later processing.
166///
167/// Instead of processing events immediately during physics step, this collector sends them
168/// to channels that you can poll from your game loop. This is the recommended approach.
169///
170/// # Example
171/// ```
172/// # use rapier3d::prelude::*;
173/// use std::sync::mpsc::channel;
174///
175/// let (collision_send, collision_recv) = channel();
176/// let (contact_force_send, contact_force_recv) = channel();
177/// let event_handler = ChannelEventCollector::new(collision_send, contact_force_send);
178///
179/// // After physics step:
180/// while let Ok(collision_event) = collision_recv.try_recv() {
181/// match collision_event {
182/// CollisionEvent::Started(h1, h2, _) => println!("Collision!"),
183/// CollisionEvent::Stopped(h1, h2, _) => println!("Separated"),
184/// }
185/// }
186/// ```
187#[cfg(feature = "std")]
188pub struct ChannelEventCollector {
189 collision_event_sender: std::sync::mpsc::Sender<CollisionEvent>,
190 contact_force_event_sender: std::sync::mpsc::Sender<ContactForceEvent>,
191}
192
193#[cfg(feature = "std")]
194impl ChannelEventCollector {
195 /// Initialize a new collision event handler from channel senders.
196 pub fn new(
197 collision_event_sender: std::sync::mpsc::Sender<CollisionEvent>,
198 contact_force_event_sender: std::sync::mpsc::Sender<ContactForceEvent>,
199 ) -> Self {
200 Self {
201 collision_event_sender,
202 contact_force_event_sender,
203 }
204 }
205}
206
207#[cfg(feature = "std")]
208impl EventHandler for ChannelEventCollector {
209 fn handle_collision_event(
210 &self,
211 _bodies: &RigidBodySet,
212 _colliders: &ColliderSet,
213 event: CollisionEvent,
214 _: Option<&ContactPair>,
215 ) {
216 let _ = self.collision_event_sender.send(event);
217 }
218
219 fn handle_contact_force_event(
220 &self,
221 dt: Real,
222 _bodies: &RigidBodySet,
223 _colliders: &ColliderSet,
224 contact_pair: &ContactPair,
225 total_force_magnitude: Real,
226 ) {
227 let result = ContactForceEvent::from_contact_pair(dt, contact_pair, total_force_magnitude);
228 let _ = self.contact_force_event_sender.send(result);
229 }
230}