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