Skip to main content
GameDev.net gamedev.net
🔒 Locked

FPS Collision Detection/Response with Stairs

Started by val9 May 24 at 10:50 PM 8 replies 1.7k views
Original Post
val9
val9

Hi guys, I've done a good amount of research in to (swept sphere/ellipse) FPS collision detection/response with stairs and it seems the accepted solution is to teleport the player up when there is a collision. Unfortunately, the videos I've seen don't go in to detail about the issues that arise and I wonder if they're anything more than monetized YouTube content. My main issue is when you collide with a step and teleport up (positive y) you aren't actually on the next step and gravity kicks in causing an edge collision with the step you just teleported up to. The only way I could get it to work was to not only teleport up but also forward but that causes jarring motion, even if smoothed, which is unacceptable. Hopefully someone can help. Thanks a lot.

Here is a little 2D diagram of what I mean.

a) player is moving right and collides with front of step.
b) player is teleported up to just above the step height.
c) gravity starts bringing player down before it's fully on the new step which causes an edge collision with the top of the step.

It's really not a problem when heading straight towards the steps, but when going slowly, like wall-sliding while climbing the steps, it breaks by causing all kinds of bouncing and won't even climb up when going very slowly.


Aressera
Aressera

I think this kind of capsule-based character controller is highly inferior to ray-cast rigid-body controllers. It will never work well because it's all a hack layered upon hack. A capsule sliding along the ground will never work right or look good.

This kind of problem is already solved by physics engines. Instead of re-implementing a hacky version, use the battle-tested capabilites of rigid body collision detection and dynamics.

What I would suggest:

  • Treat the character as a dynamic (not kinematic) rigid body, with some suitable collision volume for the body only (not including legs!).

  • Trace rays down from the body hip area toward the ground to find good foot holds (e.g. normal should be pointing close to up).

  • Apply forces/torques so that the body stays upright when in contact with the ground, and floats at the right height off the ground (the leg length), relative to the footholds.

  • Apply forces/torques so that the body moves and rotates in the direction the player wants.

  • The head where camera is attached should be rotated opposite the motion of the body, so that it stays pointed in the player's desired direction. You can clamp the rotation to a realistic one relative to the body, to avoid head pointing backwards.

This is what I've implemented for my engine/game and it works very nicely, producing a very smooth ride over very rough terrain with lots of obstacles. The key is that the body is suspended above the ground by leg forces, and everything is simulated physically. It also gives you footholds that are needed for inverse kinematics, footstep sounds/particle effects.

JoeJ
JoeJ

val9 wrote:

The only way I could get it to work was to not only teleport up but also forward but that causes jarring motion, even if smoothed, which is unacceptable.

Instead of moving forward, you could move only up but only by the exact amount needed, so the capsule touches the edge of the step.

This can be done by tracing a ray, for example:

You could then lower the ellipse by the length of the red ray.

Or you can compress the scene geometry height so the ellipse becomes a sphere, making it easier to calculate the required displacement.

The behavior of this also avoids the teleport - it shifts upwards gradually as you move forward over the edge.
But it will still feel pretty discontinuous, because it moves up most steeply in the first moment of collision.
A capsule would behave smoother because it's round not elliptical at the bottom.
But ofc. you can (and imo should) smooth it out by temporally filtering the camera height.

The method which causes a really harsh teleport as you say is likely using cylinders or just a box like Quake did.

Using a dynamic rigid body controller like @Aressera proposes keeps the discontinuity the lowest, because the body does not just move upwards but also back, as one would expect from IRL physics.
Though, it it's not without problems either. Physics engines generally allow intersections, so when the character jumps down from large height, it will intersect. The engine must minimize the intersection over time, which can cause the character to bounce off upwards, even if material restitution to zero.
Because of this i could not make a proper Super Mario controller with a dynamic rigid body. And i guess that's why most people seem to use a kinematic body, which i did not really want and have not tried.

That just said to point out each approach has its own issues. I would never say that physics engines are 'battle-tested and robust'. They just barely work well enough to use them. If you're lucky. And i would say they are overkill if you care just about a character vs. static environment. Physics engines are needed if you also want multi body physics, e.g. ragdolls or stacks of boxes.

Vilem Otte
Vilem Otte

One of the things we used to handle stairs collision was to have different collision model for stairs from its visual geometry. I don't have visualization of that right now, but let me borrow one from a modded map in Counter Strike:

I'm not sure if it's entirely visible- but red collision geometry is there placed as a slope, not stairs to ensure camera movement is smooth.

Why do you want to do this?

Because it solves two problems at once:

  1. Ensuring that your character controller step height is high enough to walk onto stairs

  2. Making character cameras smooth and not 'jumpy'

I haven't seen any other robust solutions for the above with physics engines (and I've went through quite a few over my career, and even wrote from scratch). Even if you get your character controller to work reliably with stairs, interpolating camera to reduce 'jumpiness' is close to impossible (and has so many corner cases where you could end in a wall with camera, etc.). Plus compared to stupid solution of slope it is harder to implement and worse in every possible way.

Aressera
Aressera

JoeJ wrote:

The engine must minimize the intersection over time, which can cause the character to bounce off upwards, even if material restitution to zero.

This problem is long ago solved by the "split impulse" method, where position constraint resolution (e.g. interpenetration) is resolved by an instantaneous velocity (that doesn't carry over to the next time step). Because this "bias velocity" is zeroed every step, there is no undesired bouncing, merely a smooth push out the collision that stops as soon as collision is resolved.

Vilem Otte
Vilem Otte

Aressera wrote:

This problem is long ago solved by the "split impulse" method, where position constraint resolution (e.g. interpenetration) is resolved by an instantaneous velocity (that doesn't carry over to the next time step). Because this "bias velocity" is zeroed every step, there is no undesired bouncing, merely a smooth push out the collision that stops as soon as collision is resolved.

I always found this unnatural in motion compared to using just sloped collider instead ... I don't know why but it always seemed a bit more visible than just slope.

Maybe I never saw well tuned implementation.

val9
val9

Hi guys, I should have written that I'm using swept unit sphere collision detection, I keep forgetting because my functions automatically convert the character ellipsoid and geometry. I'm now using a totally seperate collision mesh as Vilem suggested, so I have more flexibility. I also implemented a little ray caster so I can shoot one from anywhere and see if it hits anything, it's good for testing.

I think there is something wrong with my collision response function. All my problems arise when I'm sliding along a wall and hit a step. I'm using the code from the paper Improving the Numerical Robustness of Sphere Swept Collision Detection by Jeff Linahan, I don't think it's a great paper but did expect it to work.

I pretty much do this:

  1. compute new velocity based on input.

  2. collision detection/response with velocity y set to zero.

  3. collision detection/response with velocity set to (0.0, -1.0, 0.0) for gravity.

I also set up a ramp and get stuck, it happens when sliding along the wall and sticks right where the ramp starts, just like it did with the steps. I don't know what to do at this point. Thanks again.

val9
val9

Hi guys, I was actually able to get everything working fairly well. I did a bunch of hacks and special cases, it's not pretty but good enough at this point. Thanks a lot for the help.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.