bevy_xpbd_2d/components/
mod.rs

1//! Commonly used components.
2
3mod forces;
4mod layers;
5mod locked_axes;
6mod mass_properties;
7mod rotation;
8mod world_queries;
9
10pub use forces::*;
11pub use layers::*;
12pub use locked_axes::*;
13pub use mass_properties::*;
14pub use rotation::*;
15pub use world_queries::*;
16
17use crate::prelude::*;
18use bevy::prelude::*;
19use derive_more::From;
20
21/// A non-deformable body used for the simulation of most physics objects.
22///
23/// ## Rigid body types
24///
25/// A rigid body can be either dynamic, kinematic or static.
26///
27/// - **Dynamic bodies** are similar to real life objects and are affected by forces and contacts.
28/// - **Kinematic bodies** can only be moved programmatically, which is useful for things like character controllers and moving platforms.
29/// - **Static bodies** can not move, so they can be good for objects in the environment like the ground and walls.
30///
31/// ## Creation
32///
33/// Creating a rigid body is as simple as adding the [`RigidBody`] component:
34///
35/// ```
36/// use bevy::prelude::*;
37#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
38#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
39///
40/// fn setup(mut commands: Commands) {
41///     // Spawn a dynamic rigid body and specify its position (optional)
42///     commands.spawn((
43///         RigidBody::Dynamic,
44///         TransformBundle::from_transform(Transform::from_xyz(0.0, 3.0, 0.0)),
45///     ));
46/// }
47/// ```
48///
49/// Bevy XPBD will automatically add any missing components, like the following:
50///
51/// - [`Position`]
52/// - [`Rotation`]
53/// - [`LinearVelocity`]
54/// - [`AngularVelocity`]
55/// - [`ExternalForce`]
56/// - [`ExternalTorque`]
57/// - [`ExternalImpulse`]
58/// - [`ExternalAngularImpulse`]
59/// - [`Friction`]
60/// - [`Restitution`]
61/// - [`Mass`]
62/// - [`Inertia`]
63/// - [`CenterOfMass`]
64///
65/// You can change any of these during initialization and runtime in order to alter the behaviour of the body.
66///
67/// By default, rigid bodies will get a mass based on their collider and density.
68/// See [mass properties](#mass-properties).
69///
70/// ## Movement
71///
72/// A rigid body can be moved in three ways: by modifying its position directly,
73/// by changing its velocity, or by applying forces or impulses.
74///
75/// To change the position of a rigid body, you can simply modify its `Transform`:
76///
77/// ```
78/// use bevy::prelude::*;
79#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
80#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
81///
82/// fn move_bodies(mut query: Query<&mut Transform, With<RigidBody>>) {
83///     for mut transform in query.iter_mut() {
84///         transform.translation.x += 0.1;
85///     }
86/// }
87/// ```
88///
89/// However, moving a dynamic body by changing its position directly is similar
90/// to teleporting the body, which can result in unexpected behavior since the body can move
91/// inside walls.
92///
93/// You can instead change the velocity of a dynamic or kinematic body with the [`LinearVelocity`]
94/// and [`AngularVelocity`] components:
95///
96/// ```
97/// use bevy::prelude::*;
98#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
99#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
100///
101/// fn accelerate_bodies(mut query: Query<(&mut LinearVelocity, &mut AngularVelocity)>) {
102///     for (mut linear_velocity, mut angular_velocity) in query.iter_mut() {
103///         linear_velocity.x += 0.05;
104#[cfg_attr(feature = "2d", doc = "        angular_velocity.0 += 0.05;")]
105#[cfg_attr(feature = "3d", doc = "        angular_velocity.z += 0.05;")]
106///     }
107/// }
108/// ```
109///
110/// For applying forces and impulses to dynamic bodies, see the [`ExternalForce`], [`ExternalTorque`],
111/// [`ExternalImpulse`] and [`ExternalAngularImpulse`] components.
112///
113/// Bevy XPBD does not have a built-in character controller, so if you need one,
114/// you will need to implement it yourself or use a third party option.
115/// You can take a look at the [`basic_dynamic_character`] and [`basic_kinematic_character`]
116/// examples for a simple implementation.
117///
118/// [`basic_dynamic_character`]: https://github.com/Jondolf/bevy_xpbd/blob/42fb8b21c756a7f4dd91071597dc251245ddaa8f/crates/bevy_xpbd_3d/examples/basic_dynamic_character.rs
119/// [`basic_kinematic_character`]: https://github.com/Jondolf/bevy_xpbd/blob/42fb8b21c756a7f4dd91071597dc251245ddaa8f/crates/bevy_xpbd_3d/examples/basic_kinematic_character.rs
120///
121/// ## Mass properties
122///
123/// The mass properties of a rigid body consist of its [`Mass`], [`Inertia`]
124/// and local [`CenterOfMass`]. They control how forces and torques impact a rigid body
125/// and how it affects other bodies.
126///
127/// You should always give dynamic rigid bodies mass properties so that forces
128/// are applied to them correctly. The easiest way to do that is to simply [add a collider](Collider):
129///
130/// ```
131/// # use bevy::prelude::*;
132#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
133#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
134/// #
135/// # fn setup(mut commands: Commands) {
136#[cfg_attr(
137    feature = "2d",
138    doc = "commands.spawn((RigidBody::Dynamic, Collider::circle(0.5)));"
139)]
140#[cfg_attr(
141    feature = "3d",
142    doc = "commands.spawn((RigidBody::Dynamic, Collider::sphere(0.5)));"
143)]
144/// # }
145/// ```
146///
147/// This will automatically compute the [collider's mass properties](ColliderMassProperties)
148/// and add them to the body's own mass properties.
149///
150/// By default, each collider has a density of `1.0`. This can be configured with
151/// the [`ColliderDensity`] component:
152///
153/// ```
154/// # use bevy::prelude::*;
155#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
156#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
157/// #
158/// # fn setup(mut commands: Commands) {
159/// commands.spawn((
160///     RigidBody::Dynamic,
161#[cfg_attr(feature = "2d", doc = "    Collider::circle(0.5),")]
162#[cfg_attr(feature = "3d", doc = "    Collider::sphere(0.5),")]
163///     ColliderDensity(2.5),
164/// ));
165/// # }
166/// ```
167///
168/// If you don't want to add a collider, you can instead add a [`MassPropertiesBundle`]
169/// with the mass properties computed from a collider shape using the
170/// [`MassPropertiesBundle::new_computed`](MassPropertiesBundle::new_computed) method.
171///
172/// ```
173/// # use bevy::prelude::*;
174#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
175#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
176/// #
177/// # fn setup(mut commands: Commands) {
178/// // This is equivalent to the earlier approach, but no collider will be added
179/// commands.spawn((
180///     RigidBody::Dynamic,
181#[cfg_attr(
182    feature = "2d",
183    doc = "    MassPropertiesBundle::new_computed(&Collider::circle(0.5), 2.5),"
184)]
185#[cfg_attr(
186    feature = "3d",
187    doc = "    MassPropertiesBundle::new_computed(&Collider::sphere(0.5), 2.5),"
188)]
189/// ));
190/// # }
191/// ```
192///
193/// You can also specify the exact values of the mass properties by adding the components manually.
194/// To avoid the collider mass properties from being added to the body's own mass properties,
195/// you can simply set the collider's density to zero.
196///
197/// ```
198/// # use bevy::prelude::*;
199#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
200#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
201/// #
202/// # fn setup(mut commands: Commands) {
203/// // Create a rigid body with a mass of 5.0 and a collider with no mass
204/// commands.spawn((
205///     RigidBody::Dynamic,
206#[cfg_attr(feature = "2d", doc = "    Collider::circle(0.5),")]
207#[cfg_attr(feature = "3d", doc = "    Collider::sphere(0.5),")]
208///     ColliderDensity(0.0),
209///     Mass(5.0),
210///     // ...the rest of the mass properties
211/// ));
212/// # }
213///
214/// ```
215///
216/// ## See more
217///
218/// - [Colliders](Collider)
219/// - [Gravity] and [gravity scale](GravityScale)
220/// - [Linear](LinearDamping) and [angular](AngularDamping) velocity damping
221/// - [Lock translational and rotational axes](LockedAxes)
222/// - [Dominance]
223/// - [Automatic deactivation with sleeping](Sleeping)
224#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq)]
225#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
226#[reflect(Component)]
227pub enum RigidBody {
228    /// Dynamic bodies are bodies that are affected by forces, velocity and collisions.
229    #[default]
230    Dynamic,
231
232    /// Static bodies are not affected by any forces, collisions or velocity, and they act as if they have an infinite mass and moment of inertia.
233    /// The only way to move a static body is to manually change its position.
234    ///
235    /// Collisions with static bodies will affect dynamic bodies, but not other static bodies or kinematic bodies.
236    ///
237    /// Static bodies are typically used for things like the ground, walls and any other objects that you don't want to move.
238    Static,
239
240    /// Kinematic bodies are bodies that are not affected by any external forces or collisions.
241    /// They will realistically affect colliding dynamic bodies, but not other kinematic bodies.
242    ///
243    /// Unlike static bodies, kinematic bodies can have velocity.
244    /// The engine doesn't modify the values of a kinematic body's components,
245    /// so you have full control of them.
246    Kinematic,
247}
248
249impl RigidBody {
250    /// Checks if the rigid body is dynamic.
251    pub fn is_dynamic(&self) -> bool {
252        *self == Self::Dynamic
253    }
254
255    /// Checks if the rigid body is static.
256    pub fn is_static(&self) -> bool {
257        *self == Self::Static
258    }
259
260    /// Checks if the rigid body is kinematic.
261    pub fn is_kinematic(&self) -> bool {
262        *self == Self::Kinematic
263    }
264}
265
266/// Indicates that a [rigid body](RigidBody) is not simulated by the physics engine until woken up again.
267/// This is done to improve performance and to help prevent small jitter that is typically present in collisions.
268///
269/// Bodies are marked as sleeping when their linear and angular velocity is below the [`SleepingThreshold`] for a time
270/// indicated by [`DeactivationTime`]. A sleeping body is woken up when an active body interacts with it through
271/// collisions or other constraints, or when gravity changes, or when the body's
272/// position, rotation, velocity, or external forces are modified.
273///
274/// Sleeping can be disabled for specific entities with the [`SleepingDisabled`] component,
275/// or for all entities by setting the [`SleepingThreshold`] to a negative value.
276#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, From)]
277#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
278#[reflect(Component)]
279pub struct Sleeping;
280
281/// How long the velocity of the body has been below the [`SleepingThreshold`],
282/// i.e. how long the body has been able to sleep.
283///
284/// See [`Sleeping`] for further information.
285#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)]
286#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
287#[reflect(Component)]
288pub struct TimeSleeping(pub Scalar);
289
290/// Indicates that the body can not be deactivated by the physics engine. See [`Sleeping`] for information about sleeping.
291#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, From)]
292#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
293#[reflect(Component)]
294pub struct SleepingDisabled;
295
296/// The global position of a [rigid body](RigidBody) or a [collider](Collider).
297///
298/// ## Relation to `Transform` and `GlobalTransform`
299///
300/// [`Position`] is used for physics internally and kept in sync with `Transform`
301/// by the [`SyncPlugin`]. It rarely needs to be used directly in your own code, as `Transform` can still
302/// be used for almost everything. Using [`Position`] should only be required for managing positions
303/// in systems running in the [`SubstepSchedule`]. However, if you prefer, you can also use [`Position`]
304/// for everything.
305///
306/// The reasons why the engine uses a separate [`Position`] component can be found
307/// [here](crate#why-are-there-separate-position-and-rotation-components).
308///
309/// ## Example
310///
311/// ```
312/// use bevy::prelude::*;
313#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
314#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
315///
316/// fn setup(mut commands: Commands) {
317///     commands.spawn((
318///         RigidBody::Dynamic,
319#[cfg_attr(feature = "2d", doc = "         Position::from_xy(0.0, 20.0),")]
320#[cfg_attr(feature = "3d", doc = "         Position::from_xyz(0.0, 2.0, 0.0),")]
321///     ));
322/// }
323/// ```
324#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
325#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
326#[reflect(Component)]
327pub struct Position(pub Vector);
328
329impl Position {
330    /// Creates a [`Position`] component with the given global `position`.
331    pub fn new(position: Vector) -> Self {
332        Self(position)
333    }
334
335    /// Creates a [`Position`] component with the global position `(x, y)`.
336    #[cfg(feature = "2d")]
337    pub fn from_xy(x: Scalar, y: Scalar) -> Self {
338        Self(Vector::new(x, y))
339    }
340
341    /// Creates a [`Position`] component with the global position `(x, y, z)`.
342    #[cfg(feature = "3d")]
343    pub fn from_xyz(x: Scalar, y: Scalar, z: Scalar) -> Self {
344        Self(Vector::new(x, y, z))
345    }
346}
347
348impl From<GlobalTransform> for Position {
349    #[cfg(feature = "2d")]
350    fn from(value: GlobalTransform) -> Self {
351        Self::from_xy(
352            value.translation().adjust_precision().x,
353            value.translation().adjust_precision().y,
354        )
355    }
356
357    #[cfg(feature = "3d")]
358    fn from(value: GlobalTransform) -> Self {
359        Self::from_xyz(
360            value.translation().adjust_precision().x,
361            value.translation().adjust_precision().y,
362            value.translation().adjust_precision().z,
363        )
364    }
365}
366
367impl From<&GlobalTransform> for Position {
368    #[cfg(feature = "2d")]
369    fn from(value: &GlobalTransform) -> Self {
370        Self::from_xy(
371            value.translation().adjust_precision().x,
372            value.translation().adjust_precision().y,
373        )
374    }
375
376    #[cfg(feature = "3d")]
377    fn from(value: &GlobalTransform) -> Self {
378        Self::from_xyz(
379            value.translation().adjust_precision().x,
380            value.translation().adjust_precision().y,
381            value.translation().adjust_precision().z,
382        )
383    }
384}
385
386/// The position of a [rigid body](RigidBody) at the start of a substep.
387#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
388#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
389#[reflect(Component)]
390pub struct PreviousPosition(pub Vector);
391
392/// Translation accumulated during a sub-step.
393///
394/// When updating position during integration or constraint solving, the required translation
395/// is added to [`AccumulatedTranslation`], instead of [`Position`]. This improves numerical stability
396/// of the simulation, especially for bodies far away from world origin.
397///
398/// After each substep, actual [`Position`] is updated during [`SubstepSet::ApplyTranslation`].
399#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
400#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
401#[reflect(Component)]
402pub struct AccumulatedTranslation(pub Vector);
403
404/// The linear velocity of a [rigid body](RigidBody).
405///
406/// ## Example
407///
408/// ```
409/// use bevy::prelude::*;
410#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
411#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
412///
413/// fn accelerate_bodies(mut query: Query<&mut LinearVelocity>) {
414///     for mut linear_velocity in query.iter_mut() {
415///         linear_velocity.x += 0.05;
416///     }
417/// }
418/// ```
419#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
420#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
421#[reflect(Component)]
422pub struct LinearVelocity(pub Vector);
423
424impl LinearVelocity {
425    /// Zero linear velocity.
426    pub const ZERO: LinearVelocity = LinearVelocity(Vector::ZERO);
427}
428
429/// The linear velocity of a [rigid body](RigidBody) before the velocity solve is performed.
430#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
431#[reflect(Component)]
432pub(crate) struct PreSolveLinearVelocity(pub Vector);
433
434/// The angular velocity of a [rigid body](RigidBody) in radians per second.
435/// Positive values will result in counterclockwise rotation.
436///
437/// ## Example
438///
439/// ```
440/// use bevy::prelude::*;
441/// use bevy_xpbd_2d::prelude::*;
442///
443/// fn increase_angular_velocities(mut query: Query<&mut AngularVelocity>) {
444///     for mut angular_velocity in query.iter_mut() {
445///         angular_velocity.0 += 0.05;
446///     }
447/// }
448/// ```
449#[cfg(feature = "2d")]
450#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)]
451#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
452#[reflect(Component)]
453pub struct AngularVelocity(pub Scalar);
454
455/// The angular velocity of a [rigid body](RigidBody) as a rotation axis
456/// multiplied by the angular speed in radians per second.
457///
458/// ## Example
459///
460/// ```
461/// use bevy::prelude::*;
462/// use bevy_xpbd_3d::prelude::*;
463///
464/// fn increase_angular_velocities(mut query: Query<&mut AngularVelocity>) {
465///     for mut angular_velocity in query.iter_mut() {
466///         angular_velocity.z += 0.05;
467///     }
468/// }
469/// ```
470#[cfg(feature = "3d")]
471#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
472#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
473#[reflect(Component)]
474pub struct AngularVelocity(pub Vector);
475
476impl AngularVelocity {
477    /// Zero angular velocity.
478    #[cfg(feature = "2d")]
479    pub const ZERO: AngularVelocity = AngularVelocity(0.0);
480    /// Zero angular velocity.
481    #[cfg(feature = "3d")]
482    pub const ZERO: AngularVelocity = AngularVelocity(Vector::ZERO);
483}
484
485/// The angular velocity of a [rigid body](RigidBody) in radians per second, before
486/// the velocity solve is performed. Positive values will result in counterclockwise rotation.
487#[cfg(feature = "2d")]
488#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)]
489#[reflect(Component)]
490pub(crate) struct PreSolveAngularVelocity(pub Scalar);
491
492/// The angular velocity of a [rigid body](RigidBody) as a rotation axis
493/// multiplied by the angular speed in radians per second, before the velocity solve is performed.
494#[cfg(feature = "3d")]
495#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
496#[reflect(Component)]
497pub(crate) struct PreSolveAngularVelocity(pub Vector);
498
499/// Controls how [gravity](Gravity) affects a specific [rigid body](RigidBody).
500///
501/// A gravity scale of `0.0` will disable gravity, while `2.0` will double the gravity.
502/// Using a negative value will flip the direction of the gravity.
503///
504/// ## Example
505///
506/// ```
507/// use bevy::prelude::*;
508#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
509#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
510///
511/// // Spawn a body with 1.5 times the normal gravity
512/// fn setup(mut commands: Commands) {
513///     commands.spawn((
514///         RigidBody::Dynamic,
515///         GravityScale(1.5),
516///     ));
517/// }
518/// ```
519#[derive(
520    Component, Reflect, Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deref, DerefMut, From,
521)]
522#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
523#[reflect(Component)]
524pub struct GravityScale(pub Scalar);
525
526/// Determines how coefficients are combined for [`Restitution`] and [`Friction`].
527/// The default is `Average`.
528///
529/// When combine rules clash with each other, the following priority order is used:
530/// `Max > Multiply > Min > Average`.
531#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
532#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
533pub enum CoefficientCombine {
534    // The discriminants allow priority ordering to work automatically via comparison methods
535    /// Coefficients are combined by computing their average.
536    #[default]
537    Average = 1,
538    /// Coefficients are combined by choosing the smaller coefficient.
539    Min = 2,
540    /// Coefficients are combined by computing their product.
541    Multiply = 3,
542    /// Coefficients are combined by choosing the larger coefficient.
543    Max = 4,
544}
545
546/// A component for the [coefficient of restitution](https://en.wikipedia.org/wiki/Coefficient_of_restitution).
547/// This controls how bouncy a [rigid body](RigidBody) is.
548///
549/// The coefficient is between 0 and 1, where 0 corresponds to a **perfectly inelastic collision**, and 1 corresponds
550/// to a **perfectly elastic collision** that preserves all kinetic energy. The default coefficient is 0.3, and it currently
551/// can not be configured at a global level.
552///
553/// When two bodies collide, their restitution coefficients are combined using the specified [`CoefficientCombine`] rule.
554///
555/// ## Example
556///
557/// Create a new [`Restitution`] component with a restitution coefficient of 0.4:
558///
559/// ```ignore
560/// Restitution::new(0.4)
561/// ```
562///
563/// Configure how two restitution coefficients are combined with [`CoefficientCombine`]:
564///
565/// ```ignore
566/// Restitution::new(0.4).with_combine_rule(CoefficientCombine::Multiply)
567/// ```
568///
569/// Combine the properties of two [`Restitution`] components:
570///
571/// ```
572#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
573#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
574///
575/// let first = Restitution::new(0.8).with_combine_rule(CoefficientCombine::Average);
576/// let second = Restitution::new(0.5).with_combine_rule(CoefficientCombine::Multiply);
577///
578/// // CoefficientCombine::Multiply has higher priority, so the coefficients are multiplied
579/// assert_eq!(
580///     first.combine(second),
581///     Restitution::new(0.4).with_combine_rule(CoefficientCombine::Multiply)
582/// );
583/// ```
584#[doc(alias = "Bounciness")]
585#[doc(alias = "Elasticity")]
586#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)]
587#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
588#[reflect(Component)]
589pub struct Restitution {
590    /// The [coefficient of restitution](https://en.wikipedia.org/wiki/Coefficient_of_restitution).
591    ///
592    /// This should be between 0 and 1, where 0 corresponds to a **perfectly inelastic collision**, and 1 corresponds
593    /// to a **perfectly elastic collision** that preserves all kinetic energy. The default value is 0.3.
594    pub coefficient: Scalar,
595    /// The coefficient combine rule used when two bodies collide.
596    pub combine_rule: CoefficientCombine,
597}
598
599impl Restitution {
600    /// A restitution coefficient of 0.0 and a combine rule of [`CoefficientCombine::Average`].
601    ///
602    /// This is equivalent to [`Restitution::PERFECTLY_INELASTIC`](#associatedconstant.PERFECTLY_INELASTIC).
603    pub const ZERO: Self = Self {
604        coefficient: 0.0,
605        combine_rule: CoefficientCombine::Average,
606    };
607
608    /// A restitution coefficient of 0.0, which corresponds to a perfectly inelastic collision.
609    ///
610    /// Uses [`CoefficientCombine::Average`].
611    pub const PERFECTLY_INELASTIC: Self = Self {
612        coefficient: 0.0,
613        combine_rule: CoefficientCombine::Average,
614    };
615
616    /// A restitution coefficient of 1.0, which corresponds to a perfectly elastic collision.
617    ///
618    /// Uses [`CoefficientCombine::Average`].
619    pub const PERFECTLY_ELASTIC: Self = Self {
620        coefficient: 1.0,
621        combine_rule: CoefficientCombine::Average,
622    };
623
624    /// Creates a new [`Restitution`] component with the given restitution coefficient.
625    pub fn new(coefficient: Scalar) -> Self {
626        Self {
627            coefficient: coefficient.clamp(0.0, 1.0),
628            combine_rule: CoefficientCombine::Average,
629        }
630    }
631
632    /// Sets the [`CoefficientCombine`] rule used.
633    pub fn with_combine_rule(&self, combine_rule: CoefficientCombine) -> Self {
634        Self {
635            combine_rule,
636            ..*self
637        }
638    }
639
640    /// Combines the properties of two [`Restitution`] components.
641    pub fn combine(&self, other: Self) -> Self {
642        // Choose rule with higher priority
643        let rule = self.combine_rule.max(other.combine_rule);
644
645        Self {
646            coefficient: match rule {
647                CoefficientCombine::Average => (self.coefficient + other.coefficient) * 0.5,
648                CoefficientCombine::Min => self.coefficient.min(other.coefficient),
649                CoefficientCombine::Multiply => self.coefficient * other.coefficient,
650                CoefficientCombine::Max => self.coefficient.max(other.coefficient),
651            },
652            combine_rule: rule,
653        }
654    }
655}
656
657impl Default for Restitution {
658    fn default() -> Self {
659        Self {
660            coefficient: 0.3,
661            combine_rule: CoefficientCombine::default(),
662        }
663    }
664}
665
666impl From<Scalar> for Restitution {
667    fn from(coefficient: Scalar) -> Self {
668        Self {
669            coefficient,
670            ..default()
671        }
672    }
673}
674
675/// Controls how strongly the material of an entity prevents relative tangential movement at contact points.
676///
677/// For surfaces that are at rest relative to each other, static friction is used.
678/// Once the static friction is overcome, the bodies will start sliding relative to each other, and dynamic friction is applied instead.
679///
680/// 0.0: No friction at all, the body slides indefinitely\
681/// 1.0: High friction\
682///
683/// ## Example
684///
685/// Create a new [`Friction`] component with dynamic and static friction coefficients of 0.4:
686///
687/// ```ignore
688/// Friction::new(0.4)
689/// ```
690///
691/// Set the other friction coefficient:
692///
693/// ```ignore
694/// // 0.4 static and 0.6 dynamic
695/// Friction::new(0.4).with_dynamic_coefficient(0.6)
696/// // 0.4 dynamic and 0.6 static
697/// Friction::new(0.4).with_static_coefficient(0.6)
698/// ```
699///
700/// Configure how the friction coefficients of two [`Friction`] components are combined with [`CoefficientCombine`]:
701///
702/// ```ignore
703/// Friction::new(0.4).with_combine_rule(CoefficientCombine::Multiply)
704/// ```
705///
706/// Combine the properties of two [`Friction`] components:
707///
708/// ```
709#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
710#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
711///
712/// let first = Friction::new(0.8).with_combine_rule(CoefficientCombine::Average);
713/// let second = Friction::new(0.5).with_combine_rule(CoefficientCombine::Multiply);
714///
715/// // CoefficientCombine::Multiply has higher priority, so the coefficients are multiplied
716/// assert_eq!(
717///     first.combine(second),
718///     Friction::new(0.4).with_combine_rule(CoefficientCombine::Multiply)
719/// );
720/// ```
721#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)]
722#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
723#[reflect(Component)]
724pub struct Friction {
725    /// Coefficient of dynamic friction.
726    pub dynamic_coefficient: Scalar,
727    /// Coefficient of static friction.
728    pub static_coefficient: Scalar,
729    /// The coefficient combine rule used when two bodies collide.
730    pub combine_rule: CoefficientCombine,
731}
732
733impl Friction {
734    /// Zero dynamic and static friction and [`CoefficientCombine::Average`].
735    pub const ZERO: Self = Self {
736        dynamic_coefficient: 0.0,
737        static_coefficient: 0.0,
738        combine_rule: CoefficientCombine::Average,
739    };
740
741    /// Creates a new `Friction` component with the same dynamic and static friction coefficients.
742    pub fn new(friction_coefficient: Scalar) -> Self {
743        Self {
744            dynamic_coefficient: friction_coefficient,
745            static_coefficient: friction_coefficient,
746            ..default()
747        }
748    }
749
750    /// Sets the [`CoefficientCombine`] rule used.
751    pub fn with_combine_rule(&self, combine_rule: CoefficientCombine) -> Self {
752        Self {
753            combine_rule,
754            ..*self
755        }
756    }
757
758    /// Sets the coefficient of dynamic friction.
759    pub fn with_dynamic_coefficient(&self, coefficient: Scalar) -> Self {
760        Self {
761            dynamic_coefficient: coefficient,
762            ..*self
763        }
764    }
765
766    /// Sets the coefficient of static friction.
767    pub fn with_static_coefficient(&self, coefficient: Scalar) -> Self {
768        Self {
769            static_coefficient: coefficient,
770            ..*self
771        }
772    }
773
774    /// Combines the properties of two `Friction` components.
775    pub fn combine(&self, other: Self) -> Self {
776        // Choose rule with higher priority
777        let rule = self.combine_rule.max(other.combine_rule);
778        let (dynamic1, dynamic2) = (self.dynamic_coefficient, other.dynamic_coefficient);
779        let (static1, static2) = (self.static_coefficient, other.static_coefficient);
780
781        Self {
782            dynamic_coefficient: match rule {
783                CoefficientCombine::Average => (dynamic1 + dynamic2) * 0.5,
784                CoefficientCombine::Min => dynamic1.min(dynamic2),
785                CoefficientCombine::Multiply => dynamic1 * dynamic2,
786                CoefficientCombine::Max => dynamic1.max(dynamic2),
787            },
788            static_coefficient: match rule {
789                CoefficientCombine::Average => (static1 + static2) * 0.5,
790                CoefficientCombine::Min => static1.min(static2),
791                CoefficientCombine::Multiply => static1 * static2,
792                CoefficientCombine::Max => static1.max(static2),
793            },
794            combine_rule: rule,
795        }
796    }
797}
798
799impl Default for Friction {
800    fn default() -> Self {
801        Self {
802            dynamic_coefficient: 0.3,
803            static_coefficient: 0.3,
804            combine_rule: CoefficientCombine::default(),
805        }
806    }
807}
808
809impl From<Scalar> for Friction {
810    fn from(coefficient: Scalar) -> Self {
811        Self {
812            dynamic_coefficient: coefficient,
813            static_coefficient: coefficient,
814            ..default()
815        }
816    }
817}
818
819/// Automatically slows down a dynamic [rigid body](RigidBody), decreasing its
820/// [linear velocity](LinearVelocity) each frame. This can be used to simulate air resistance.
821///
822/// The default linear damping coefficient is `0.0`, which corresponds to no damping.
823///
824/// ## Example
825///
826/// ```
827/// use bevy::prelude::*;
828#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
829#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
830///
831/// fn setup(mut commands: Commands) {
832///     commands.spawn((
833///         RigidBody::Dynamic,
834///         LinearDamping(0.8),
835///     ));
836/// }
837/// ```
838#[derive(
839    Component, Reflect, Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deref, DerefMut, From,
840)]
841#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
842#[reflect(Component)]
843pub struct LinearDamping(pub Scalar);
844
845/// Automatically slows down a dynamic [rigid body](RigidBody), decreasing its
846/// [angular velocity](AngularVelocity) each frame. This can be used to simulate air resistance.
847///
848/// The default angular damping coefficient is `0.0`, which corresponds to no damping.
849///
850/// ## Example
851///
852/// ```
853/// use bevy::prelude::*;
854#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
855#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
856///
857/// fn setup(mut commands: Commands) {
858///     commands.spawn((
859///         RigidBody::Dynamic,
860///         AngularDamping(1.6),
861///     ));
862/// }
863/// ```
864#[derive(
865    Component, Reflect, Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deref, DerefMut, From,
866)]
867#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
868#[reflect(Component)]
869pub struct AngularDamping(pub Scalar);
870
871/// **Dominance** allows [dynamic rigid bodies](RigidBody::Dynamic) to dominate
872/// each other during physical interactions.
873/// 
874/// The body with a higher dominance acts as if it had infinite mass, and will be unaffected during
875/// collisions and other interactions, while the other body will be affected normally.
876/// 
877/// The dominance must be between `-127` and `127`, and the default value is `0`.
878/// Note that static and kinematic bodies will always have a higher dominance value
879/// than dynamic bodies regardless of the value of this component.
880/// 
881/// ## Example
882/// 
883/// ```
884/// use bevy::prelude::*;
885#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
886#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
887///
888/// // Player dominates all dynamic bodies with a dominance lower than 5
889/// fn spawn_player(mut commands: Commands) {
890///     commands.spawn((
891///         RigidBody::Dynamic,
892///         Collider::capsule(1.0, 0.4),
893///         Dominance(5),
894///     ));
895/// }
896/// ```
897#[rustfmt::skip]
898#[derive(Component, Reflect, Debug, Clone, Copy, Default, Deref, DerefMut, From, PartialEq, PartialOrd, Eq, Ord)]
899#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
900#[reflect(Component)]
901pub struct Dominance(pub i8);
902
903#[cfg(test)]
904mod tests {
905    use crate::prelude::*;
906    use approx::assert_relative_eq;
907
908    #[test]
909    fn restitution_clamping_works() {
910        assert_eq!(Restitution::new(-2.0).coefficient, 0.0);
911        assert_eq!(Restitution::new(0.6).coefficient, 0.6);
912        assert_eq!(Restitution::new(3.0).coefficient, 1.0);
913    }
914
915    #[test]
916    fn coefficient_combine_works() {
917        let r1 = Restitution::new(0.3).with_combine_rule(CoefficientCombine::Average);
918
919        // (0.3 + 0.7) / 2.0 == 0.5
920        let average_result =
921            r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Average));
922        let average_expected = Restitution::new(0.5).with_combine_rule(CoefficientCombine::Average);
923        assert_relative_eq!(
924            average_result.coefficient,
925            average_expected.coefficient,
926            epsilon = 0.0001
927        );
928        assert_eq!(average_result.combine_rule, average_expected.combine_rule);
929
930        // 0.3.min(0.7) == 0.3
931        assert_eq!(
932            r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Min)),
933            Restitution::new(0.3).with_combine_rule(CoefficientCombine::Min)
934        );
935
936        // 0.3 * 0.7 == 0.21
937        let multiply_result =
938            r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Multiply));
939        let multiply_expected =
940            Restitution::new(0.21).with_combine_rule(CoefficientCombine::Multiply);
941        assert_relative_eq!(
942            multiply_result.coefficient,
943            multiply_expected.coefficient,
944            epsilon = 0.0001
945        );
946        assert_eq!(multiply_result.combine_rule, multiply_expected.combine_rule);
947
948        // 0.3.max(0.7) == 0.7
949        assert_eq!(
950            r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Max)),
951            Restitution::new(0.7).with_combine_rule(CoefficientCombine::Max)
952        );
953    }
954}