Creating a delay after being grounded so the player can still jump while falling.

Hey everyone, I have a tiny problem that I can’t solve ( noob at scripting, yes ).

2D platformer - jumping. While grounded check works perfectly, everyone knows that back in the old days this was a known thing : if you’re falling from the edge of an object ( the one you had grounded = true a split second ago ), player can’t jump and falls down to his death.

What I am trying to do is to add a very short delay between player being grounded and not being grounded anymore so that the player can still jump while he’s not physically on the edge anymore.

Can anyone help me with this, please?

Here’s my code if someone has a minute to spare…

`

public float moveSpeed;
private float activeMoveSpeed;

public bool canMove;

public Rigidbody2D myRigidBody;

public float jumpSpeed;

public Transform groundCheck;
public float groundCheckRadius;
public LayerMask whatIsGround;

public bool isGrounded;

private Animator myAnim;

public Vector3 respawnPosition;

private bool onPlatform;
public float onPlatformSpeedModifier;

public LevelManager theLevelManager;

public GameObject stompBox;

public float knockbackForce;
public float knockbackLength;
private float knockbackCounter;

public float invincibilityLength;
private float invincibilityCounter;

public AudioSource playerJumpSound;
public AudioSource playerHurtSound;

// Start is called before the first frame update
void Start()
{
    myRigidBody = GetComponent<Rigidbody2D>();
    myAnim = GetComponent<Animator>();

    respawnPosition = transform.position;

    theLevelManager = FindObjectOfType<LevelManager>();

    activeMoveSpeed = moveSpeed;

    canMove = true;

}

// Update is called once per frame
void Update()
{

    isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);

    

    if (knockbackCounter <= 0 && canMove)
    {
        if (onPlatform)
        {
            activeMoveSpeed = moveSpeed * onPlatformSpeedModifier;
        }
        else
        {
            activeMoveSpeed = moveSpeed;
        }

        if (Input.GetAxisRaw("Horizontal") > 0f)
        //if (CrossPlatformInputManager.GetAxis("Horizontal") > 0f)
        {
            myRigidBody.velocity = new Vector3(activeMoveSpeed, myRigidBody.velocity.y, 0f);
            transform.localScale = new Vector3(1f, 1f, 1f);
        }
        else if (Input.GetAxisRaw("Horizontal") < 0f)
        //else if (CrossPlatformInputManager.GetAxis("Horizontal") < 0f)
        {
            myRigidBody.velocity = new Vector3(-activeMoveSpeed, myRigidBody.velocity.y, 0f);
            transform.localScale = new Vector3(-1f, 1f, 1f);
        }
        else
        {
            myRigidBody.velocity = new Vector3(0f, myRigidBody.velocity.y, 0f);
        }

        if (Input.GetButtonDown("Jump") && isGrounded)
        //if (CrossPlatformInputManager.GetButtonDown("Jump") && isGrounded)
        {
            myRigidBody.velocity = new Vector3(myRigidBody.velocity.x, jumpSpeed, 0f);
            playerJumpSound.Play();
        }

    }

    if(knockbackCounter > 0)
    {
        knockbackCounter -= Time.deltaTime;

        if (transform.localScale.x > 0)
        {
            myRigidBody.velocity = new Vector3(-knockbackForce * 3f, knockbackForce, 0f);
        }
        else
        {
            myRigidBody.velocity = new Vector3(knockbackForce * 3f, knockbackForce, 0f);
        }    
    }

   
    if (invincibilityCounter > 0)
    {
        invincibilityCounter -= Time.deltaTime;
    }

    if(invincibilityCounter <= 0)
    {
        theLevelManager.invincible = false;
    }

    myAnim.SetFloat("Speed", Mathf.Abs(myRigidBody.velocity.x));
    myAnim.SetBool("Grounded", isGrounded);

    if(myRigidBody.velocity.y < 0)
    {
        stompBox.SetActive(true);
    }
    else
    {
        stompBox.SetActive(false);
    }

}
public void Knockback()
{
    knockbackCounter = knockbackLength;
    invincibilityCounter = invincibilityLength;
    theLevelManager.invincible = true;
}

public IEnumerator RespawnAfterFall()
{
    yield return new WaitForSeconds(2f);
    transform.position = respawnPosition;

}

private void OnTriggerEnter2D(Collider2D other)
{
    if(other.tag == "KillPlane")
    {
        
        StartCoroutine("RespawnAfterFall");

        //gameObject.SetActive(false);

        //transform.position = respawnPosition;

        //theLevelManager.Respawn();
    }   

    if(other.tag == "Checkpoint")
    {
        respawnPosition = other.transform.position;
    }
}

private void OnCollisionEnter2D(Collision2D other)
{
    if(other.gameObject.tag == "MovingPlatform")
    {
        transform.parent = other.transform;
        onPlatform = true;
    }
}

private void OnCollisionExit2D(Collision2D other)
{
    if(other.gameObject.tag == "MovingPlatform")
    {
        transform.parent = null;
        onPlatform = false;
    }
}

}

`

First add a public float secondChanceTime (this variable will represent short delay you want, set that in the inspector) and maybe a private bool secondChance and set it default to true; then somewhere in that giant Update block (might want to break out each function into their own to make a little cleaner) add this line:

if(isGrounded != true)
{
Invoke("SecondChance", secondChanceTime);
}

Then somewhere outside of that update block add this:

//this function can either be public or private, depends on if other scripts need to know about the secondChance change

public void SecondChance()
{
secondChance = false;
}

Then change jump function to something like this:

if (Input.GetButtonDown("Jump") && secondChance == true)

And then don’t forget to add something back to the isGrounded to make secondChance true again, and that will give you the short window to jump again if you fall off platform.

Just declare a cooldown / timer variable which takes care of the “signal extension”.

public float jumpTimeExtension = 0.5f;
float jumpCondition = 0;

void Update()
{
    // [ ... ]
    if (isGrounded)
        jumpCondition = jumpTimeExtension;
    if (jumpCondition > 0)
    {
        jumpCondition -= Time.deltaTime;
        if (Input.GetButtonDown("Jump"))
        {
            myRigidBody.velocity = new Vector3(myRigidBody.velocity.x, jumpSpeed, 0f);
            playerJumpSound.Play();    
        }
    }
    // [ ... ]
}

So while “jumpCondition” is greater than 0 you can jump. As long as you are grounded it’s set to “jumpTimeExtension” every frame and therefore greater than 0. Since we subtract deltaTime each frame it naturally drops over time if it’s not reset every frame. So once the time has run out you can no longer jump. As soon as you get back to ground the timer resets immediately.