bevy_rapier2d/control/character_controller.rs
1use crate::geometry::{Collider, CollisionGroups, ShapeCastHit};
2use crate::math::{Real, Rot, Vect};
3use bevy::prelude::*;
4
5use crate::plugin::context::RapierContextColliders;
6pub use rapier::control::CharacterAutostep;
7pub use rapier::control::CharacterLength;
8use rapier::prelude::{ColliderSet, QueryFilterFlags};
9
10/// A collision between the character and its environment during its movement.
11#[derive(Copy, Clone, PartialEq, Debug)]
12pub struct CharacterCollision {
13 /// The entity hit by the character.
14 pub entity: Entity,
15 /// The position of the character when the collider was hit.
16 pub character_translation: Vect,
17 /// The rotation of the character when the collider was hit.
18 pub character_rotation: Rot,
19 /// The translation that was already applied to the character when the hit happens.
20 pub translation_applied: Vect,
21 /// The translations that was still waiting to be applied to the character when the hit happens.
22 pub translation_remaining: Vect,
23 /// Geometric information about the hit.
24 pub hit: ShapeCastHit,
25}
26
27impl CharacterCollision {
28 pub(crate) fn from_raw(
29 ctxt: &RapierContextColliders,
30 c: &rapier::control::CharacterCollision,
31 ) -> Option<Self> {
32 Self::from_raw_with_set(&ctxt.colliders, c, true)
33 }
34
35 pub(crate) fn from_raw_with_set(
36 colliders: &ColliderSet,
37 c: &rapier::control::CharacterCollision,
38 details_always_computed: bool,
39 ) -> Option<Self> {
40 RapierContextColliders::collider_entity_with_set(colliders, c.handle).map(|entity| {
41 CharacterCollision {
42 entity,
43 character_translation: c.character_pos.translation,
44 #[cfg(feature = "dim2")]
45 character_rotation: c.character_pos.rotation.angle(),
46 #[cfg(feature = "dim3")]
47 character_rotation: c.character_pos.rotation,
48 translation_applied: c.translation_applied,
49 translation_remaining: c.translation_remaining,
50 hit: ShapeCastHit::from_rapier(c.hit, details_always_computed),
51 }
52 })
53 }
54}
55
56/// Options for moving a shape using `RapierContext::move_shape`.
57#[derive(Clone, Debug, Copy, PartialEq)]
58pub struct MoveShapeOptions {
59 /// The direction that goes "up". Used to determine where the floor is, and the floor’s angle.
60 pub up: Vect,
61 /// A small gap to preserve between the character and its surroundings.
62 ///
63 /// This value should not be too large to avoid visual artifacts, but shouldn’t be too small
64 /// (must not be zero) to improve numerical stability of the character controller.
65 pub offset: CharacterLength,
66 /// Should the character try to slide against the floor if it hits it?
67 pub slide: bool,
68 /// Should the character automatically step over small obstacles?
69 pub autostep: Option<CharacterAutostep>,
70 /// The maximum angle (radians) between the floor’s normal and the `up` vector that the
71 /// character is able to climb.
72 pub max_slope_climb_angle: Real,
73 /// The minimum angle (radians) between the floor’s normal and the `up` vector before the
74 /// character starts to slide down automatically.
75 pub min_slope_slide_angle: Real,
76 /// Should the character apply forces to dynamic bodies in its path?
77 pub apply_impulse_to_dynamic_bodies: bool,
78 /// Should the character be automatically snapped to the ground if the distance between
79 /// the ground and its feet are smaller than the specified threshold?
80 pub snap_to_ground: Option<CharacterLength>,
81 /// Increase this number if your character appears to get stuck when sliding against surfaces.
82 ///
83 /// This is a small distance applied to the movement toward the contact normals of shapes hit
84 /// by the character controller. This helps shape-casting not getting stuck in an always-penetrating
85 /// state during the sliding calculation.
86 ///
87 /// This value should remain fairly small since it can introduce artificial "bumps" when sliding
88 /// along a flat surface.
89 pub normal_nudge_factor: Real,
90}
91
92impl Default for MoveShapeOptions {
93 fn default() -> Self {
94 let def = rapier::control::KinematicCharacterController::default();
95 Self {
96 up: def.up,
97 offset: def.offset,
98 slide: def.slide,
99 autostep: def.autostep,
100 max_slope_climb_angle: def.max_slope_climb_angle,
101 min_slope_slide_angle: def.min_slope_slide_angle,
102 apply_impulse_to_dynamic_bodies: true,
103 snap_to_ground: def.snap_to_ground,
104 normal_nudge_factor: def.normal_nudge_factor,
105 }
106 }
107}
108
109/// A character controller for kinematic bodies and free-standing colliders.
110#[derive(Clone, Debug, Component)] // TODO: Reflect
111pub struct KinematicCharacterController {
112 /// The translations we desire the character to move by if it doesn’t meet any obstacle.
113 pub translation: Option<Vect>,
114 /// The shape, and its position, to be used instead of the shape of the collider attached to
115 /// the same entity is this `KinematicCharacterController`.
116 pub custom_shape: Option<(Collider, Vect, Rot)>,
117 /// The mass to be used for impulse of dynamic bodies. This replaces the mass of the rigid-body
118 /// potentially associated to the collider attached to the same entity as this
119 /// `KinematicCharacterController`.
120 ///
121 /// This field isn’t used if `Self::apply_impulse_to_dynamic_bodies` is set to `false`.
122 pub custom_mass: Option<Real>,
123 /// The direction that goes "up". Used to determine where the floor is, and the floor’s angle.
124 pub up: Vect,
125 /// A small gap to preserve between the character and its surroundings.
126 ///
127 /// This value should not be too large to avoid visual artifacts, but shouldn’t be too small
128 /// (must not be zero) to improve numerical stability of the character controller.
129 pub offset: CharacterLength,
130 /// Should the character try to slide against the floor if it hits it?
131 pub slide: bool,
132 /// Should the character automatically step over small obstacles?
133 pub autostep: Option<CharacterAutostep>,
134 /// The maximum angle (radians) between the floor’s normal and the `up` vector that the
135 /// character is able to climb.
136 pub max_slope_climb_angle: Real,
137 /// The minimum angle (radians) between the floor’s normal and the `up` vector before the
138 /// character starts to slide down automatically.
139 pub min_slope_slide_angle: Real,
140 /// Should the character apply forces to dynamic bodies in its path?
141 pub apply_impulse_to_dynamic_bodies: bool,
142 /// Should the character be automatically snapped to the ground if the distance between
143 /// the ground and its feet are smaller than the specified threshold?
144 pub snap_to_ground: Option<CharacterLength>,
145 /// Flags for filtering-out some categories of entities from the environment seen by the
146 /// character controller.
147 pub filter_flags: QueryFilterFlags,
148 /// Groups for filtering-out some colliders from the environment seen by the character
149 /// controller.
150 pub filter_groups: Option<CollisionGroups>,
151 /// Increase this number if your character appears to get stuck when sliding against surfaces.
152 ///
153 /// This is a small distance applied to the movement toward the contact normals of shapes hit
154 /// by the character controller. This helps shape-casting not getting stuck in an always-penetrating
155 /// state during the sliding calculation.
156 ///
157 /// This value should remain fairly small since it can introduce artificial "bumps" when sliding
158 /// along a flat surface.
159 pub normal_nudge_factor: Real,
160}
161
162impl KinematicCharacterController {
163 pub(crate) fn to_raw(&self) -> Option<rapier::control::KinematicCharacterController> {
164 let autostep = self.autostep.map(|autostep| CharacterAutostep {
165 max_height: autostep.max_height,
166 min_width: autostep.min_width,
167 include_dynamic_bodies: autostep.include_dynamic_bodies,
168 });
169
170 if self.up.length_squared() < 1.0e-12 {
171 return None;
172 }
173
174 Some(rapier::control::KinematicCharacterController {
175 up: self.up.normalize(),
176 offset: self.offset,
177 slide: self.slide,
178 autostep,
179 max_slope_climb_angle: self.max_slope_climb_angle,
180 min_slope_slide_angle: self.min_slope_slide_angle,
181 snap_to_ground: self.snap_to_ground,
182 normal_nudge_factor: self.normal_nudge_factor,
183 })
184 }
185}
186
187impl Default for KinematicCharacterController {
188 fn default() -> Self {
189 let def = rapier::control::KinematicCharacterController::default();
190 Self {
191 translation: None,
192 custom_shape: None,
193 custom_mass: None,
194 up: def.up,
195 offset: def.offset,
196 slide: def.slide,
197 autostep: def.autostep,
198 max_slope_climb_angle: def.max_slope_climb_angle,
199 min_slope_slide_angle: def.min_slope_slide_angle,
200 apply_impulse_to_dynamic_bodies: true,
201 snap_to_ground: def.snap_to_ground,
202 filter_flags: QueryFilterFlags::default() | QueryFilterFlags::EXCLUDE_SENSORS,
203 filter_groups: None,
204 normal_nudge_factor: def.normal_nudge_factor,
205 }
206 }
207}
208
209/// The output of a character control.
210///
211/// This component is automatically added after the first execution of a character control
212/// based on the `KinematicCharacterController` component with its
213/// `KinematicCharacterController::translation` set to a value other than `None`.
214#[derive(Clone, PartialEq, Debug, Default, Component)]
215pub struct KinematicCharacterControllerOutput {
216 /// Indicates whether the shape is grounded after its kinematic movement.
217 pub grounded: bool,
218 /// The initial desired movement of the character if there were no obstacle.
219 pub desired_translation: Vect,
220 /// The translation calculated by the last character control step taking obstacles into account.
221 pub effective_translation: Vect,
222 /// Collisions between the character and obstacles found in its path.
223 pub collisions: Vec<CharacterCollision>,
224 /// Indicates whether the shape is sliding down a slope after its kinematic movement.
225 pub is_sliding_down_slope: bool,
226}
227
228/// The allowed movement computed by `RapierContext::move_shape`.
229pub struct MoveShapeOutput {
230 /// Indicates whether the shape is grounded after its kinematic movement.
231 pub grounded: bool,
232 /// The translation calculated by the last character control step taking obstacles into account.
233 pub effective_translation: Vect,
234 /// Indicates whether the shape is sliding down a slope after its kinematic movement.
235 pub is_sliding_down_slope: bool,
236}