Skip to content
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

KinematicCharacterController doesn't respect offest #613

Open
cleak opened this issue Mar 31, 2024 · 0 comments
Open

KinematicCharacterController doesn't respect offest #613

cleak opened this issue Mar 31, 2024 · 0 comments

Comments

@cleak
Copy link

cleak commented Mar 31, 2024

The offset field in KinematiCharacterController isn't respected - see the video below. I've found two places which contribute to this, both in KinematiCharacterController::move_shape. In this function offset is used to back off from a collision after it's detected, which appears to have two major failure points at the moment:

1. The backoff amount is calculated incorrectly

At glancing angles, this backoff gets smaller rather than larger. The code currently has:

let allowed_dist =
    (toi.toi - (-toi.normal1.dot(&translation_dir)) * offset).max(0.0);

but division of the dot product rather than multiplication was likely intended to normalize the distance, such as:

let allowed_dist =
    (toi.toi - offset / (-toi.normal1.dot(&translation_dir))).max(0.0);

A direct approach like this introduces its own problems at near -90 degree angles of course, so I wouldn't advocate for this verbatim. It also only solves part of the problem.

2. Glancing angles may fail to detect any collision into the offset area

I wasn't able to capture this directly in a debugger, but from inspection it appears that penetration into the KCC's offset area can happen at glancing angles. This is because the offset is applied to the distance of the shape cast rather than widening the shape itself:

// 2. Cast towards the movement direction.
if let Some((handle, toi)) = queries.cast_shape(
    bodies,
    colliders,
    &(Translation::from(result.translation) * character_pos),
    &translation_dir,
    character_shape,
    translation_dist + offset,
    false,
    filter,
) {

specifically:

translation_dist + offset,

This distance may fail to penetrate an adjacent wall, so no result is returned, while still ending up less than offset away from it laterally. This part doesn't have a quick arithmetic fix since the extra term here should be dependent on the collision normal like in (1).

I don't know enough about Rapier internals to confidently propose a fix to this portion, but let me know if this sounds reasonable and I can create a PR:

  1. Create a new shape that's extended by the calculated offset. I'm a little fuzzy on how to do this since it seems to require casting to the concrete shape type and using knowledge of each shape to grow it. Potentially I can add a clone_and_grow function to the Shape trait.
  2. Use this larger shape in cast_shape. Pretty straight forward.
  3. Recalculate TOI based on the smaller shape. I'm not sure if there's an efficient way to do this. Worst case, the distance can be calculated from the intersection with the larger the shape and cast_shape can be called a second time. This seems inefficient obviously but will only happen when there are potential collisions to handle.

Video of this happening: https://youtu.be/q46BHkLa_s4
Code example (you'll need to uncomment offset): https://github.com/cleak/RapierWallTest/blob/master/src/main.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant