Charactercontroller and walking down a stairs

I have made a charactercontroller that is based at the one that can be found in the Unity Manual.

My script has already a few more lines. But not this much.The problem that i have is that when i walk down a stairs, then the character is in air for short moments again and again. Means it stutters visibly, and changes into the jump animation. Changing directions is very slow by that too.

Ive tried to go around. My idea was to implement a buffer value, so that the character is still counted as grounded when he is in air for a short moment. And failed miserably. My approach was to go across two variables and a flag. One variable counts up when not grounded. And when the variable is bigger than the second variable, lets say it has 5, then the boolean gets set to false. And else it is true. So far so good.

The character refuses to jump when i replace the if (controller.isGrounded) if (!controller.isGrounded) by if (myFlag=false) and if (myFlag=true). I tried several ways, my character always refuses to jump then. I am lost :slight_smile:

What is the way to go here? Where am i wrong?

There is an examplefile with the character and a stairs attached. 10 Mb.

1 Like

Well, for a start it seems that your character is in air even when he walks on the plane.
If you put a
Debug.Log(controller.isGrounded);
before the if in movement script you will see that the condition is sometimes false though the character is walking a plane.

1 Like

Here’s what I’ve done to combat that.

Essentially, when on the ground, force the player down by the character controller’s “step height” multiplied by deltaTime. That’ll force it down when it goes down slopes or stairs and prevent the floating/falling as you go off the edge of each one. It then gets set to 0 once when the player goes over a ledge that’s too big, after that, regular gravity is applied to increase the speed until terminal velocity.

The grounded, gravity and movementspeed are specific to my code, but should be easy enough to swap out for your own code.

if ( collision.grounded ) {
	// Regular, on the ground, gravity.
	// Force player downwards the distance of a step as defined by the character controller. Prevents leaving the ground when going down slopes.
	movement.currentSpeed.y = -charController.stepOffset / Time.deltaTime;
}
else if ( !collision.grounded  collision.wasGrounded  movement.currentSpeed.y < 0 ) {
	// Just left ground, not moving upwards (i.e. jumping), so we've just starting falling.
	// Set downwards speed to 0, otherwise we'll be forced downwards at nearly half terminal velocity.
	movement.currentSpeed.y = 0.0;
}
else {
	// Regular falling gravity.
	// Increase downwards speed until terminal velocity is reached.
	movement.currentSpeed.y = Mathf.Min ( movement.currentSpeed.y - ( physics.gravity * Time.deltaTime ), physics.terminalVelocity );
}

Thanks scarpelius, that`s indeed the problem. As told it is in air for a short moment when walking down a slope.

Thanks farfarer, ill have a look if i can figure out and incorporate your idea. Im still a bit lost with writing code :slight_smile:

Got it working. Solution method is the same. Pull the player downwards. But i use transform.Translate now. To add to move made too much trouble, and gave not enough control. The character rotated much better, but still tried to change to the jump animation while the jump itself was not longer possible because the value was already too big. Curious. Most probably me doing something wrong again. But one working method is enough :slight_smile:

Addition to the code in the movement.js in the example file is now as followed. This comes into the variables part:

// -------------------------- stairsfix variables ------------

var grounded: int=0;
var slopefix: float=8.0;

And at the top of the function update, below var controller … i have added:

			if (controller.isGrounded) grounded=0;
			if (!controller.isGrounded) grounded +=1;
			
			if (grounded==1)
                                transform.Translate(0,slopefix*Time.deltaTime*-1, 0);

Works like charm. Many thanks for the hint :slight_smile:

Yes, it might work like that but is diminishing the jump height.

Heh, that was more my problem with where i tried to implement the solution from Farfarer, there it decreased the jump height too much. My solution does of course also decrease the jump height by the value the character gets pulled down. But i can live with that. My slopes are not this strong :slight_smile:

Try to avoid using Translate or any tranform functions / properties to change anything about the transform ( its okay to get the values, just dont modify your transform unless you want to teleport your character collider ).
Always use either CharacterCollider.Move or CharacterCollider.SimpleMove to handle movements.

With that said though going down stairs or ramps is probably more of a task than going up them. SimpleMove in general which applies gravity automatically is good at keeping things on the ground but since it uses Physics.gravity it can be hit or miss and you may need to change gravity to get it to work properly.

Its important to remember though, the way CharacterController works. Its not a rigidbody so all physics updates are done within the call to either Move or SimpleMove. That being said you could apply more downward force when isGrounded is true to stick the player to the ground while going down stairs because the isGrounded var will be true before you apply the move. The down side is if you want people to fall off things they were walking on they’ll at least at first make a bigger jump downward and wont just fly off the platform like in say mario. But if your not dealing with alot of running off of things its pretty much a win win.

So try putting more force downward when isGrounded is true before you call Move or SimpleMove and not when its false.

Keep in mind though, isGrounded or the collision flags they do not change until either Move or SimpleMove is called. So do not worry about them changing on you unless you’ve got moving platforms or do a teleport

Thanks BDev,

Hm. That`s all nice in theory, and a good advice. But in practice i have already move applied. It is not recommend to add move more than one time. And every try to modify the move makes unbelievable lots of trouble. I had my fun with a simple jump already. To deal with move may be the better method, but the simpler method is to use transform.Translate here. And it is working good enough for now. The core idea is the same, no matter if i modify move or use transform.translate. Pull the player downwards so that it stays grounded at slopes.

I´m open for every working solution though, and happy about every code snippet that teaches me something new. The example is attached. How would the movement script need to be modified? :slight_smile:

I’m not suggesting to call move more than once. But before you call it once per frame you can see if it was put on ground by the last frame. Its not suggested to call it more than one because it actually does all the physics and sends the messages.

Maybe transform.Translate works in the fact theres a character controller, but you can just as easily call Move with the same parameters just transform.TranslateDirection to get the vector of what translate would apply. Sorry don’t have more time to look at the script

Okay, now i know why it is a better idea to use move. My character started falling through the terrain here and there, hehe.

I have replaced

                               transform.Translate(0,slopefix*Time.deltaTime*-1, 0);

by

				moveDirection += transform.TransformDirection(Vector3(0,slopefix*Time.deltaTime*-1, 0));

With a value of around 150 for the slopefix value. Fingers crossed that falling through terrain is the past now :slight_smile:

7 years later

private bool IsGrounded()
    {
        float floorDistanceFromFoot = charController.stepOffset;

        RaycastHit hit;
        if(Physics.Raycast(transform.position, Vector3.down, out hit, floorDistanceFromFoot) || charController.isGrounded){
            Debug.DrawRay(transform.position, Vector3.down * floorDistanceFromFoot, Color.yellow);
            return true;
        }     

        return false;
    }
2 Likes

You are a legend. Can I use your code? There is nothing to fix here and it’s perfect. I’ll be sure to credit properly.

2 Likes