Checking if a CharacterController is grounded?

Hi all. I’m finding a need to detect when my character is on the ground, for the purposes of transitioning out of a jump animation, back into a walk/run anim.

I’m using a CharacterControlller. And i was quite pleased to find that it includes an isGrounded flag, to check if yhe character is grounded.

Unfortunately, to put it mildly, it’s shit. It cannot handle even tiny variations in the ground, such as a slightly uneven terrain. And as a result, it frequently claims to not be grounded long after it’s hit the ground and slid along it for over 30 fixedUpdate frames. It seems to be pure pot luck whether it detects the ground or not, and this is resulting in a random delay of up to 2 seconds after landing, before it figures out it’s standing on the ground. This is mostly unuseable.

Does anyone have ideas for a better method? I was thinking of putting a trigger sphere on my character that goes slightly below the feet, and would keep setting the grounded bool according to whether or not it’s touching something.

However the docs say that triggers only work if one of the colliders involved has a rigidbody: Unity - Scripting API: MonoBehaviour.OnTriggerStay(Collider)

Obviously you don’t put a rigidbody on a heightmap terrain. (at least, i can’t think of any situation where that would make sense) and putting a rigidbody alongside a characterController breaks it, and causes it to fall through the world.

i need a better idea

Have you tried looking into using Physics.SphereCast or one of the similar methods? You could SphereCast downwards from the player’s position, and detect if there is ground below you and how far away it is.

I tried using a collision setup with onCollisionStay, and a slightly smaller downwards-offset sphere collider, which pokes out the bottom of the character controller
but that didn’t work either, no collisions detected. However you’ve given me an excellent idea.

I can read the sphere collider into memory at startup (then delete it for performance) and use it’s parameters for the sphere cast. This should work very well, ty Iron warrior.

I wonder why i’ve never used non-line casting functions, this seems incredibly useful
Edit: I wrote this post hours ago, forgot to hit send

Update, i’ve gotten it working.
Didn’t work at first, starting a spherecast in the place i want to check does not detect anything, apparently it doesn’t detect colliders it’s already interpenetrating at the start.

So i had to start the cast at the centre of my character controller, and set it to cast downwards to my target point. that’s worked, and i have a pretty reliable onGround checker

If you’re looking to cast a sphere with the same radius as the CC, you can retrieve the CC’s radius at runtime (instead of using a prebuilt sphere collider that is deleted at startup).

If you’re looking at building a more complex character and need extremely robust ground detection, I wrote a fairly lengthy post on the topic (and am still researching the best way to do this).

Thank you, this is a very interesting post. i’m reading and bookmarking it for later.

In this context i’m just making a simple demo, but i will need complex ground detection in the future

I am having a little bit of an issue though. This spherecasting method is more accurate, but it still has a little inaccuracy. Checking if the character is on ground returns true, for the first few frames after leaving the ground, due to the cast extending below the character.

I suppose i can ameliorate this by tightening the sphere up a bit so it protrudes less. But doesn’t this ultimately make the code framerate-dependant? The higher the framerate is, the less it’ll be moving in each frame, and hence the more accurate the spherecast will have to be to avoid false positives.

You seem like you’ve researched a lot into this, what’s your thoughts?

Hello, you can compare your character bounds min Y value against the hit point Y value of the sphere cast. Still wont be 100% accurate in slopes but should be a lot closer to reality.

That’s the right idea.

For my method I use a SphereCast with infinite range, and then test against the RaycastHit.distance to see if the controller should count as grounded. To resolve this:

I only allow the controller to become grounded depending on it’s current state. (I’m assuming when you say “for the first few frames after leaving the ground…” you are referring to him jumping.) When the controller enters the Jumping state, it only checks to see if it can become grounded if the player is moving downwards. This avoids the issue you stated above, since as you said at high frame rates the controller would jump and then immediately sense the ground beneath him.