Inconsistent Raycast Detection

I’m working on a 2.5d platformer that, due to it’s exaggerated movements, will require custom physics. To handle this, I’m using raycasting to detect player collisions with the ground, walls, platforms, etc. Detecting walls is working fine, but I’m finding my landings from jumps to be quite inconsistent.

The jumps are a Lerped transform to a certain height, followed by a reduction in the Y axis location until a downward raycast hits the ground. To avoid my player dropping through the ground, I start the raycast midway up the player. The problem is my player will sometimes land with his feet under the mesh (to varying degrees). It’s quite inconsistent. Here’s a code snippet showing the ground interaction:

	void FixedUpdate()
	{
		//fall
		if (isFalling)
		{
			float downSpeed = transform.position.y - (35.0f * Time.fixedDeltaTime);
			transform.position = new Vector3 (transform.position.x, downSpeed, transform.position.z);
		}

		//Debug.Log(groundingRay.position.y);

		//ground contact
		Vector3 groundOffset = new Vector3(0.0f, -2.5f, 0.0f); //used for debugging
		Ray groundRay = new Ray(groundingRay.position, groundOffset); //groundingRay is a transform
		RaycastHit groundHit;

		if (Physics.Raycast(groundRay, out groundHit, 2.5f))
		{
			isFalling = false;
			isGrounded = true;
			//Debug.Log("I'm grounded");
			//Debug.DrawLine(groundingRay.position, groundingRay.position + groundOffset, Color.red);
		}
    }

I notice that if I adjust the rate in which he falls from 35.0f to 40.0f, the falls are much less chaotic. Which makes me wonder if I should be “tuning” the size of my platforms to a multiple of 40 (or whatever I deem the most consistent) to ensure collisions are detected more accurately.

Another ideas was extend the ray slightly, get the mesh attached to the ground (or platform) collider, detect the northern most vertex in line with my raycast, and set that as a coordinate for my player to stop at. Assuming I can do that at all. I’d hate to introduce the extra calculations if I didn’t have to. Appreciate any help!

You fire starting at groundingRay? Is that properly updated to track the player?

Most people just start the raycast at transform.position. Or have an empty child to mark the spot: transform.Find("raycastPos").position

For fun, and testing, you can make a tiny red ball, and move it to groundHit.point.

I know this is 2 years old, but it’s still kind of an issue today.

From my test, the issue that comes around when we (you and me) are using a Raycast to detect the ground and “stop” the gravity manually is that we have to overcome the per-frame imprecision of a ever-changing float value that is the distance between the player and the ground.

You have to consider that as soon as the player is “really close” to the ground during frame X, on the next frame (X+1), he might be at 0.1 lower or even more. Raycast is called at a fixed rate so, in other word, each frame. It doesn’t consider the result between frames.

The way you can fix this is by doing a double check on the distance and force the position when within any acceptable parameters.

In my case, I’m doing such a check with a small conditional code as this :

if(DistanceToGround >=0 && DistanceToGround <= 0.2f && !isJumping){
transform.position = new Vector3 (GroundPoint.x, GroundPoint.y + 0.05f, GroundPoint.z);}

GroundPoint is the registered hit.point that is registered exactly below the player. In my case I pass the value through another parameter which explain why I use GroundPoint instead of just hit.point. With this conditional setup, whenever the player is within 0 and 0.2f unit (which is, in my case, about half of the size of the character’s foot), its position on Y automatically change to the 0.05f. The “isJumping”, as you might guess, is a simple checkup that activate when the player is jumping as we don’t want the code to affect the player when he’s trying to jump.

You might be able to have it smaller like 0.1f or 0.15f, but in my case, the player can drop fast enough to cover up to 0.15f between 2 frames so with 0.2f, I know it’s relatively safe (for now at least). At some point, I might to even store the ground position and do some kind of triple check for PCs who might have an huge drop of FPS. (I’m still not on the part about optimizing for low-end PC yet.)

By “forcing” the position when within at something like 0.2f unit of ground, this remove the jerking that usually happens with only relying to the direct Raycast feedback. Since the player usually fall at 0.15f unit per sec in my game, “forcing” the position at 0.05f doesn’t appears to the eye.

Just in case (for anyone who read this), if you allow the player to have “partial jump” (as jump that jump higher or lower based on how long they hold the jump button when jumping), I suggest you put a “minimum” jump force/height if you’re planning on using the raycast to simulate the ground physics otherwise it will look kinda strange if the player only slightly tap the jump button and the player jump an height too close to the ground checkup (in my case between 0 and 0.2f). For example, if the player jump 0.3f units, as soon as he gets within the 0.2f “poof” he’s at 0.05f. Since the momentum isn’t at its faster on the way down, it’s not the ideal. That’s why I would suggest that, in such case, the jump action should cover a minimal height of 3x or 4x the Checkup height value. (in my case, the player should not jump lower than 1 unit.)