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}