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