-
-
Notifications
You must be signed in to change notification settings - Fork 265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix character max/min slope #701
Changes from all commits
d2c9c35
5a03a09
63233f6
222c2ec
64c810e
175316b
2a04835
e4c923e
c1fe32b
9862850
a9c5b68
7204849
bccd234
8cdb21a
c6b359d
0bd887b
553d164
160fbb3
c6420f6
677ed84
98b58e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -169,6 +169,7 @@ impl Default for KinematicCharacterController { | |
} | ||
|
||
/// The effective movement computed by the character controller. | ||
#[derive(Debug)] | ||
pub struct EffectiveCharacterMovement { | ||
/// The movement to apply. | ||
pub translation: Vector<Real>, | ||
|
@@ -542,17 +543,17 @@ impl KinematicCharacterController { | |
) -> Vector<Real> { | ||
let [_vertical_input, horizontal_input] = self.split_into_components(movement_input); | ||
let horiz_input_decomp = self.decompose_hit(&horizontal_input, &hit.toi); | ||
let input_decomp = self.decompose_hit(movement_input, &hit.toi); | ||
|
||
let decomp = self.decompose_hit(translation_remaining, &hit.toi); | ||
|
||
// An object is trying to slip if the tangential movement induced by its vertical movement | ||
// points downward. | ||
let slipping_intent = self.up.dot(&horiz_input_decomp.vertical_tangent) < 0.0; | ||
// An object is slipping if its vertical movement points downward. | ||
let slipping = self.up.dot(&decomp.vertical_tangent) < 0.0; | ||
|
||
// An object is trying to climb if its indirect vertical motion points upward. | ||
let climbing_intent = self.up.dot(&input_decomp.vertical_tangent) > 0.0; | ||
// An object is trying to climb if its vertical input motion points upward. | ||
let climbing_intent = self.up.dot(&_vertical_input) > 0.0; | ||
// An object is climbing if the tangential movement induced by its vertical movement points upward. | ||
Comment on lines
-554
to
+556
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in my understanding, "climbing intent" refers to an explicit movement going upward of the slope, that's why I'm testing only the input, and not the decomposition according to the hit. the naming is confusing because slipping_intent doesn't refer exactly to the same thing, but I'm not sure how to rename. |
||
let climbing = self.up.dot(&decomp.vertical_tangent) > 0.0; | ||
|
||
let allowed_movement = if hit.is_wall && climbing && !climbing_intent { | ||
|
@@ -904,3 +905,151 @@ fn subtract_hit(translation: Vector<Real>, hit: &ShapeCastHit) -> Vector<Real> { | |
let surface_correction = surface_correction * (1.0 + 1.0e-5); | ||
translation + *hit.normal1 * surface_correction | ||
} | ||
|
||
#[cfg(all(feature = "dim3", feature = "f32"))] | ||
#[cfg(test)] | ||
mod test { | ||
use crate::{control::KinematicCharacterController, prelude::*}; | ||
|
||
#[test] | ||
fn character_controller_climb_test() { | ||
let mut colliders = ColliderSet::new(); | ||
let mut impulse_joints = ImpulseJointSet::new(); | ||
let mut multibody_joints = MultibodyJointSet::new(); | ||
let mut pipeline = PhysicsPipeline::new(); | ||
let mut bf = BroadPhaseMultiSap::new(); | ||
let mut nf = NarrowPhase::new(); | ||
let mut islands = IslandManager::new(); | ||
let mut query_pipeline = QueryPipeline::new(); | ||
|
||
let mut bodies = RigidBodySet::new(); | ||
|
||
let gravity = Vector::y() * -9.81; | ||
|
||
let ground_size = 100.0; | ||
let ground_height = 0.1; | ||
/* | ||
* Create a flat ground | ||
*/ | ||
let rigid_body = RigidBodyBuilder::fixed().translation(vector![0.0, -ground_height, 0.0]); | ||
let floor_handle = bodies.insert(rigid_body); | ||
let collider = ColliderBuilder::cuboid(ground_size, ground_height, ground_size); | ||
colliders.insert_with_parent(collider, floor_handle, &mut bodies); | ||
|
||
/* | ||
* Create a slope we can climb. | ||
*/ | ||
let slope_angle = 0.2; | ||
let slope_size = 2.0; | ||
let collider = ColliderBuilder::cuboid(slope_size, ground_height, slope_size) | ||
.translation(vector![0.1 + slope_size, -ground_height + 0.4, 0.0]) | ||
.rotation(Vector::z() * slope_angle); | ||
colliders.insert(collider); | ||
|
||
/* | ||
* Create a slope we can’t climb. | ||
*/ | ||
let impossible_slope_angle = 0.6; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. on master, this impossible angle can be climb in this test. That's what https://github.com/dimforge/rapier/pull/701/files#r1697044257 fixes. |
||
let impossible_slope_size = 2.0; | ||
let collider = ColliderBuilder::cuboid(slope_size, ground_height, ground_size) | ||
.translation(vector![ | ||
0.1 + slope_size * 2.0 + impossible_slope_size - 0.9, | ||
-ground_height + 1.7, | ||
0.0 | ||
]) | ||
.rotation(Vector::z() * impossible_slope_angle); | ||
colliders.insert(collider); | ||
|
||
let integration_parameters = IntegrationParameters::default(); | ||
|
||
// Initialize character which can climb | ||
let mut character_body_can_climb = RigidBodyBuilder::kinematic_position_based() | ||
.additional_mass(1.0) | ||
.build(); | ||
character_body_can_climb.set_translation(Vector::new(0.6, 0.5, 0.0), false); | ||
let character_handle_can_climb = bodies.insert(character_body_can_climb); | ||
|
||
let collider = ColliderBuilder::ball(0.5).build(); | ||
colliders.insert_with_parent(collider.clone(), character_handle_can_climb, &mut bodies); | ||
|
||
// Initialize character which cannot climb | ||
let mut character_body_cannot_climb = RigidBodyBuilder::kinematic_position_based() | ||
.additional_mass(1.0) | ||
.build(); | ||
character_body_cannot_climb.set_translation(Vector::new(-0.6, 0.5, 0.0), false); | ||
let character_handle_cannot_climb = bodies.insert(character_body_cannot_climb); | ||
|
||
let collider = ColliderBuilder::ball(0.5).build(); | ||
let character_shape = collider.shape(); | ||
colliders.insert_with_parent(collider.clone(), character_handle_cannot_climb, &mut bodies); | ||
|
||
query_pipeline.update(&colliders); | ||
for i in 0..200 { | ||
let mut update_character_controller = | ||
|controller: KinematicCharacterController, handle: RigidBodyHandle| { | ||
let character_body = bodies.get(handle).unwrap(); | ||
// Use a closure to handle or collect the collisions while | ||
// the character is being moved. | ||
let mut collisions = vec![]; | ||
let filter_character_controller = QueryFilter::new().exclude_rigid_body(handle); | ||
let effective_movement = controller.move_shape( | ||
integration_parameters.dt, | ||
&bodies, | ||
&colliders, | ||
&query_pipeline, | ||
character_shape, | ||
character_body.position(), | ||
Vector::new(0.1, -0.1, 0.0), | ||
filter_character_controller, | ||
|collision| collisions.push(collision), | ||
); | ||
let character_body = bodies.get_mut(handle).unwrap(); | ||
let translation = character_body.translation(); | ||
assert_eq!( | ||
effective_movement.grounded, true, | ||
"movement should be grounded at all times for current setup (iter: {}), pos: {}.", | ||
i, translation + effective_movement.translation | ||
); | ||
character_body.set_next_kinematic_translation( | ||
translation + effective_movement.translation, | ||
); | ||
}; | ||
|
||
let character_controller_cannot_climb = KinematicCharacterController { | ||
max_slope_climb_angle: impossible_slope_angle - 0.001, | ||
..Default::default() | ||
}; | ||
let character_controller_can_climb = KinematicCharacterController { | ||
max_slope_climb_angle: impossible_slope_angle + 0.001, | ||
..Default::default() | ||
}; | ||
update_character_controller( | ||
character_controller_cannot_climb, | ||
character_handle_cannot_climb, | ||
); | ||
update_character_controller(character_controller_can_climb, character_handle_can_climb); | ||
// Step once | ||
pipeline.step( | ||
&gravity, | ||
&integration_parameters, | ||
&mut islands, | ||
&mut bf, | ||
&mut nf, | ||
&mut bodies, | ||
&mut colliders, | ||
&mut impulse_joints, | ||
&mut multibody_joints, | ||
&mut CCDSolver::new(), | ||
Some(&mut query_pipeline), | ||
&(), | ||
&(), | ||
); | ||
} | ||
let character_body = bodies.get(character_handle_can_climb).unwrap(); | ||
assert!(character_body.translation().x > 6.0); | ||
assert!(character_body.translation().y > 3.0); | ||
let character_body = bodies.get(character_handle_cannot_climb).unwrap(); | ||
assert!(character_body.translation().x < 4.0); | ||
assert!(dbg!(character_body.translation().y) < 2.0); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scale to add back ; not sure it's still usable though 🤔