Unity 3d rigidbodies going upstairs

I followed this tutorial to make so that my character in my FPS game can go up stairs using Rigidbody instead of CharacterController due to it’s limitations (namely not being able to change the shape of it’s collider), and uh… It didn’t work :p.

I made another thread earlier trying to make so the player can go up stairs in all directions:

I modified the video’s solution by using a Physics.CheckBox instead of Physics.Raycast, but it just… Didn’t work. The raycast solution wasn’t working properly either.

Basically, no matter what value I put for the size of the check box, it is very glitchy and can’t really detect well the stairs, even if they are right in front of the player. I checked the code and couldn’t find any issues with it, and indeed when I make the box insanely long it does detect the stairs from afar (the player sort of keeps bumping upwards), it just can’t make the player properly climb them. I was able to go through one of the stairs I used to test in a very specific direction though.

I don’t know what the issue is so I will just put the code here and say what I know is not the issue because I tested:

    [Header("Steps and slopes")]
    public Transform StepCheckHigher; //If the step is taller than this object, it should not be climbed automatically//
    public Transform StepCheckLower; //Checks for obstacles//
    public Vector3 StepCheckHigherSize; //How far the lower step ray checks for an obstacle//
    public Vector3 StepCheckLowerSize; //How far the lower step ray checks for an obstacle//
    public float StepHeight = 0.4f; //How tall an obstacle can be so the player can step up automatically//
    public float StepAmount = 0.1f; //How much the player climbs up steps//

Inside of void Start():

        StepCheckHigher.position = new Vector3(StepCheckLower.position.x, StepCheckLower.position.y + StepHeight, StepCheckLower.position.z);

Inside of void FixedUpdate():

        if (Player.PlayerMovementStatus != PlayerStats.PlayerState.Standing)
        {
            StepClimb();
        }
    void StepClimb()
    {
        if (Physics.CheckBox(StepCheckLower.position, StepCheckLowerSize, StepCheckLower.rotation, SolidLayer))
        {
            if (!Physics.CheckBox(StepCheckHigher.position, StepCheckHigherSize, StepCheckHigher.rotation, SolidLayer))
            {
                PlayerRigidbody.position += new Vector3(0f, StepAmount * Time.deltaTime, 0f);
            }
        }
    }
  • It can’t be due to the if inside of void FixedUpdate() checking if the player is standing or not because it glitches out even while the player is clearly moving;
  • It can’t be due to the size of the check box, because even with different sizes the same issue appears;
  • It can’t be due to the player’s own collider interacting with the check box because the player moves just fine around outside of stairs;
  • It can’t be due to the StepAmount value being too low, because I already tested with different values and it still doesn’t work properly;
  • It can’t be due to the stairs themselves because I tested stairs of different sizes and shapes and they all have the same issue;

So how do I fix that?

Any ideas on what to do?

First thing to do is never move a rigidbody via transform.position or rigidbody.position.

Always use the .MovePosition() on the rigidbody so that physics is involved and gets a chance to solve stuff like collisions and events and whatnot.

I tried this:

    void StepClimb()
    {
        if (Physics.CheckBox(StepCheckLower.position, StepCheckLowerSize, StepCheckLower.rotation, SolidLayer))
        {
            if (!Physics.CheckBox(StepCheckHigher.position, StepCheckHigherSize, StepCheckHigher.rotation, SolidLayer))
            {
                PlayerRigidbody.MovePosition(transform.position + new Vector3(0f, StepAmount * Time.deltaTime, 0f));
            }
        }
    }

But it doesn’t work. By “doesn’t work”, I mean the player just can’t climb any steps at all, only when wiggling left and right quickly, but that works even without the step climber script.

I’m not sure what the step lower and step higher logic is all about but honestly I’ve always just made ramps for steps: have the actual step geometry for visuals, but then have a collider that is actually just a single straight ramp that you slide up.

It seems most FPS games do this, such as Escape from Tarkov (see about 0:20 onwards)

They do have a slight jog to the camera vertically while on the stairs, but it is FAR lower frequency than the actual stairs. You’ll notice that he does an entire flight of stairs in about 2 bobs up and down, and the bob motion is extremely subtle.

If you really want to make the camera jog up and down like it’s actually doing stairs, I would just drive the camera with an extra periodic up/down that is modulated by your forward speed and only active on stairs.

Now as for other players and getting the AnimationIK stuff going so their feet match the stairs, that’s an entire different story than getting an RB to follow it, but up to you how you want to handle it really.

So I guess this was all for nothing?

The logic was from the video I linked, I simply made so that if there is an obstacle that is detected by the lower trigger but not the higher, it will move the player upwards.

That is a mischaracterization of what I said. I simply observed that the economics of following stairs precisely seem not to be worth the effort, at least judging by the majority of games I have seen in the wild.

In the end it is your game and it is you who makes economic decisions about effort and result tradeoffs. If you want Rigidbodies to follow stairs, I suppose you should persist in this effort to get it working.

And I also know that if you are moving Rigidbodies that you WANT to collide with the world, you absolutely need to do so with .MovePosition(). That much is indisputable based on what I know and what the docs say about Unity physics.

Ok but, by going with the method I have, what can I do for it to work properly?

As always, go up the data chain.

First step would be to find out if CheckBox() is working, given your level design and your passed-in arguments.

If that’s giving good data, then figure out why your positional calculation is wrong.

If CheckBox() is not returning what you expect, find out why. Strip out your scene, make a test and prove how that function works. I’ve never used it personally. Read the docs on it, make sure of how it operates.

I already explained almost all of my thought proccess and testing in the thread, but I found something new here.

The problem is here:

    void StepClimb()
    {
        StepCheckLower.rotation = LookTransform.rotation;
        StepCheckHigher.rotation = LookTransform.rotation;

        if (Physics.CheckBox(StepCheckLower.position, StepCheckLowerSize, StepCheckLower.rotation, SolidLayer))
        {
            Debug.Log("Found obstacle");
            if (!Physics.CheckBox(StepCheckHigher.position, StepCheckHigherSize, StepCheckHigher.rotation, SolidLayer))
            {
                Debug.Log("Found step");
                PlayerRigidbody.MovePosition(transform.position + new Vector3(0f, StepAmount * Time.deltaTime, 0f));
                Debug.Log("Moved to " + (transform.position + new Vector3(0f, StepAmount * Time.deltaTime, 0f)));
            }
        }
    }

I noticed that when walking up the stairs and stopping for some reason, it has trouble moving afterwards, and using Debug.Log I noticed the first part of this code (when it triggers “Found obstacle”) works fine but the second if has trouble getting through at this occasions.

But I have no idea why this happens, there is nothing really wrong with the code or values given.

I gave up on this. I just did the ramp approach and threw all this work in the trash.