Dubugging a jumping issue

I have a piece of code that causes a character to jump in place. That’s all it does. It looks like this:

   void LateUpdate()
    {
        velocity = Vector3.zero;
       
        if (!IsGrounded())
        {
            momentum -= transform.up * gravity * Time.deltaTime;
        }

        else
        {
            momentum = Vector3.zero;
           
            if (Input.GetKeyDown(jumpKey))
            {
                momentum += transform.up * jumpSpeed;
               
            }
           
        }
       
        velocity += momentum;
        rb.velocity = velocity;
    }

It seems to work ok, but for some reason not 100% of the time. The button might need to be pressed once or several times to get the character to jump. I have:

  • Disabled the gravity on the rigidbody (rb) and froze the rotation. All other settings are default
  • The gravity is 9 and the jumpSpeed is 7
  • I’ve switched buttons/keys in case there was an input issue
  • I’ve verified that the controller is indeed grounded

I need a second pair of eyes here. Why is this not working 100% of the time?

Does it work 100% of the time in Update?

Update and LateUpdate work on the same loop so there should be no difference in function. FixedUpdate would cause intermittent input.

I suspect this has to do with how IsGrounded() is working, its the only part I can see being a problem as I don’t know what its doing.

@jeffreyjene Can you post the code for IsGrouded()? I’m sure the problem is in there somewhere.

Hmmm, I could be wrong, but try printing out your rb.velocity.

momentum -= transform.up * gravity * Time.deltaTime;
Will decrease your momentum more and more the longer your character is in the air. It will always be negative on the Y-axis because of that reason.

velocity += momentum;
Will lower your velocity by your momentum ALWAYS. Thus, I believe, that when the character hits the ground, the velocity on the Y-axis is still negative due to it never resetting.

If I’m correct, once you Debug.Log(rb.velocity) you should see a negative number on your Y-axis. To fix it, simply set the Y velocity of the rb to 0 once the player is grounded:

else
{
    momentum = Vector3.zero;
    rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z);

    if (Input.GetKeyDown(jumpKey))
    {
        momentum += transform.up * jumpSpeed;
    }
}

This would cause you to have to push the jump button faster to “over take” the downward force, so it might be the issue as well. If you let this sit for awhile you probably couldn’t press jump fast enough to even make it jump at all, and if it gets a fast enough downward force than it would start to push through the ground.

Let us know if this is the issue.

I would use built in gravity and then use rigidBody.AddForce() to jump myself to avoid such issues.

Maybe because after you press jumpKey your rb still IsGrounded().
Move your rb.velocity = velocity; line to Update()

Here is the IsGrounded code:

public bool IsGrounded()
    {
        RaycastHit hit;
        origin = collider.bounds.center;
        sensorDistance = collider.bounds.extents.y - sensorRadius + sensorOffset;
       
        if (Physics.SphereCast(origin, sensorRadius, direction, out hit, sensorDistance, layerMask, QueryTriggerInteraction.UseGlobal))
        {
            grounded = true;
            currentHitDistance = hit.distance;
        }
        else
        {
            currentHitDistance = sensorDistance;
            grounded = false;
        }

        return grounded;
    }

sensor radius is .3, collider is 2 units by .5, layermask is everything, and the offset is .01f to get it just a smidge above the ground. The controller is pretty much sitting on the spherecast.

Thanks, that isn’t the issue. I also want precise control over the character. I’m overriding the rb’s velocity during movement, so any AddForce would be zeroed out or overwritten.

SphereCast for something like this is over kill, its more processing than OverlapSphere when the overlap will do the same thing. Use SphereCast like a RayCast that needs to be wider than a Ray.

Should use layermasks to stop it from registering on other colliders. I usually setup a Ground layer and have it only look for collisions with that.

Other than that I have no idea what could be wrong. Debug.Log IsGrounded to make sure its registering the ground. Debug.Log everything really each step of the way to see what goes on, or use the debugger in VS and step through the code and have a look.

Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
bool grounded = IsGrounded();

if (grounded && Input.GetButtonDown("Jump"))
{
      rBody.AddForce(Vector3.up * jumpForce);
}

rBody.velocity = new Vector3(move.x, rBody.velocity.y, move.z);

You can use this technique in both 2D and 3D.