bevy_rapier3d/plugin/systems/
character_controller.rs1use crate::control::CharacterCollision;
2use crate::dynamics::RapierRigidBodyHandle;
3use crate::geometry::RapierColliderHandle;
4use crate::plugin::context::systemparams::RAPIER_CONTEXT_EXPECT_ERROR;
5use crate::plugin::context::RapierContextEntityLink;
6use crate::plugin::RapierConfiguration;
7use crate::prelude::context::RapierContextColliders;
8use crate::prelude::context::RapierContextSimulation;
9use crate::prelude::context::RapierRigidBodySet;
10use crate::prelude::KinematicCharacterController;
11use crate::prelude::KinematicCharacterControllerOutput;
12use crate::utils;
13use bevy::prelude::*;
14use rapier::math::Isometry;
15use rapier::math::Real;
16use rapier::parry::query::DefaultQueryDispatcher;
17use rapier::pipeline::QueryFilter;
18
19pub fn update_character_controls(
22 mut commands: Commands,
23 config: Query<&RapierConfiguration>,
24 mut context_access: Query<(
25 &mut RapierContextSimulation,
26 &mut RapierContextColliders,
27 &mut RapierRigidBodySet,
28 )>,
29 mut character_controllers: Query<(
30 Entity,
31 &RapierContextEntityLink,
32 &mut KinematicCharacterController,
33 Option<&mut KinematicCharacterControllerOutput>,
34 Option<&RapierColliderHandle>,
35 Option<&RapierRigidBodyHandle>,
36 Option<&GlobalTransform>,
37 )>,
38 mut transforms: Query<&mut Transform>,
39) {
40 for (
41 entity,
42 rapier_context_link,
43 mut controller,
44 output,
45 collider_handle,
46 body_handle,
47 glob_transform,
48 ) in character_controllers.iter_mut()
49 {
50 if let (Some(raw_controller), Some(translation)) =
51 (controller.to_raw(), controller.translation)
52 {
53 let config = config
54 .get(rapier_context_link.0)
55 .expect("Could not get [`RapierConfiguration`]");
56 let (mut context, mut context_colliders, mut rigidbody_set) = context_access
57 .get_mut(rapier_context_link.0)
58 .expect(RAPIER_CONTEXT_EXPECT_ERROR);
59
60 let context = &mut *context;
61 let rigidbody_set = &mut *rigidbody_set;
62 let scaled_custom_shape =
63 controller
64 .custom_shape
65 .as_ref()
66 .map(|(custom_shape, tra, rot)| {
67 let mut scaled_shape = custom_shape.clone();
69 scaled_shape.set_scale(custom_shape.scale, config.scaled_shape_subdivision);
70
71 (scaled_shape, *tra, *rot)
72 });
73
74 let parent_rigid_body = body_handle.map(|h| h.0).or_else(|| {
75 collider_handle
76 .and_then(|h| context_colliders.colliders.get(h.0))
77 .and_then(|c| c.parent())
78 });
79 let entity_to_move = parent_rigid_body
80 .and_then(|rb| rigidbody_set.rigid_body_entity(rb))
81 .unwrap_or(entity);
82
83 let (character_shape, character_pos) = if let Some((scaled_shape, tra, rot)) =
84 &scaled_custom_shape
85 {
86 let mut shape_pos: Isometry<Real> = (*tra, *rot).into();
87
88 if let Some(body) = body_handle.and_then(|h| rigidbody_set.bodies.get(h.0)) {
89 shape_pos = body.position() * shape_pos
90 } else if let Some(gtransform) = glob_transform {
91 shape_pos = utils::transform_to_iso(>ransform.compute_transform()) * shape_pos
92 }
93
94 (&*scaled_shape.raw, shape_pos)
95 } else if let Some(collider) =
96 collider_handle.and_then(|h| context_colliders.colliders.get(h.0))
97 {
98 (collider.shape(), *collider.position())
99 } else {
100 continue;
101 };
102 let character_shape = character_shape.clone_dyn();
103
104 let exclude_collider = collider_handle.map(|h| h.0);
105
106 let character_mass = controller
107 .custom_mass
108 .or_else(|| {
109 parent_rigid_body
110 .and_then(|h| rigidbody_set.bodies.get(h))
111 .map(|rb| rb.mass())
112 })
113 .unwrap_or(0.0);
114
115 let mut filter = QueryFilter {
116 flags: controller.filter_flags,
117 groups: controller.filter_groups.map(|g| g.into()),
118 exclude_collider: None,
119 exclude_rigid_body: None,
120 predicate: None,
121 };
122
123 if let Some(parent) = parent_rigid_body {
124 filter = filter.exclude_rigid_body(parent);
125 } else if let Some(excl_co) = exclude_collider {
126 filter = filter.exclude_collider(excl_co)
127 };
128
129 let collisions = &mut context.character_collisions_collector;
130 collisions.clear();
131
132 let mut query_pipeline = context.broad_phase.as_query_pipeline_mut(
133 &DefaultQueryDispatcher,
134 &mut rigidbody_set.bodies,
135 &mut context_colliders.colliders,
136 filter,
137 );
138 let movement = raw_controller.move_shape(
141 context.integration_parameters.dt,
142 &query_pipeline.as_ref(),
143 &*character_shape,
144 &character_pos,
145 translation.into(),
146 |c| collisions.push(c),
147 );
148
149 if controller.apply_impulse_to_dynamic_bodies {
150 raw_controller.solve_character_collision_impulses(
151 context.integration_parameters.dt,
152 &mut query_pipeline,
153 &*character_shape,
154 character_mass,
155 collisions.iter(),
156 )
157 }
158
159 if let Ok(mut transform) = transforms.get_mut(entity_to_move) {
160 transform.translation.x += movement.translation.x;
162 transform.translation.y += movement.translation.y;
163 #[cfg(feature = "dim3")]
164 {
165 transform.translation.z += movement.translation.z;
166 }
167 }
168
169 let converted_collisions = context
170 .character_collisions_collector
171 .iter()
172 .filter_map(|c| CharacterCollision::from_raw(&context_colliders, c));
173
174 if let Some(mut output) = output {
175 output.desired_translation = controller.translation.unwrap();
176 output.effective_translation = movement.translation.into();
177 output.grounded = movement.grounded;
178 output.collisions.clear();
179 output.collisions.extend(converted_collisions);
180 output.is_sliding_down_slope = movement.is_sliding_down_slope;
181 } else {
182 commands
183 .entity(entity)
184 .insert(KinematicCharacterControllerOutput {
185 desired_translation: controller.translation.unwrap(),
186 effective_translation: movement.translation.into(),
187 grounded: movement.grounded,
188 collisions: converted_collisions.collect(),
189 is_sliding_down_slope: movement.is_sliding_down_slope,
190 });
191 }
192
193 controller.translation = None;
194 }
195 }
196}