Jump buffering and Coyote time causes double jump when spamming jump

I implemented jump buffering and coyote time but when I spam the jump button, the player can do a double jump. How do I fix it?

//Ground Check
bool isGrounded;
float groundedRemember = 0;
float groundedRememberTime = .1f;

//Jump
public float jumpForce;
float jumpPressedRemember = 0;
float jumpPressedRememberTime = .2f;

How I code jump buffering and coyote time:

    if ((jumpPressedRemember > 0) && (groundedRemember > 0))
    {
        rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        jumpPressedRemember = 0;
        groundedRemember = 0;
    }

    jumpPressedRemember -= Time.deltaTime;
    if (Input.GetKeyDown(KeyCode.Space))
    {
        jumpPressedRemember = jumpPressedRememberTime;
    }

    groundedRemember -= Time.deltaTime;
    if (isGrounded == true)
    {
        groundedRemember = groundedRememberTime;
    }

I have only 2 explanations:

  1. Your isGrounded bool doesn’t change to false until you press the space bar a second time.

  2. You pressed the space bar so fast that before isGrounded variable has changed, you had the time to double jump.

The most probable one is the first explanation. Set isGrounded to public to see in your inspector when it changes, if it changes correctly or not.

By the way, what do you use to check ground collision?

Hi @ilovetoeatmeatballs , did you find the solution to that? It also happened to me when I set the jumpcooldown to a low number (something like 0.15 and below. So the problem might be what you’ve said: jump buffering and coyote time interacting with each other.

@ilovetoeatmeatballs , @GuirieSanchez

I’m a bit late, but I have the very same problem: added jumping buffer and coyote time = player occasionally double (or sometime triple) jumping. My problem seems to be caused by a pretty big delay between switching of grounded bool (~1.7 sec). I have no idea why is it so big.
I’ve handled this issue by adding an cooldown between jump inputs (basically the same system as coyote time). Here are snippets of my code (it is made for 3D, but should work with 2D too):

//Required variables    
[SerializeField] private float jumpInputDelay = 0.18f;
private float jumpInputDelayTimer = 0f;
private bool readyToJump;

//Handling cooldown
private void Update()
{
    DelayInput();
}

private void DelayInput()
{
    switch (jumpInputDelayTimer) 
    {
        case <= 0:
            readyToJump = true;
            break;
        default:
            readyToJump = false;
            jumpInputDelayTimer -= Time.deltaTime;
            break;
    }   
}

//Using cooldown to duct tape the problem
private void Jump()
{
    if ((isGrounded && !jumped || !isGrounded && coyoteTimer > 0f && !jumped) && readyToJump)
    {
        jumped = true;
        jumpInputDelayTimer = jumpInputDelay;
        StopCrouch();
        rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z);
        rb.AddForce(transform.up * _jumpForce, ForceMode.Impulse);
        coyoteTimer = 0f;
    }
    else if(!isGrounded)
    {
        AddNewActionToQuee(Jump);
    }
}

Hope it helps and sorry for the poor quality of code.