- Home /
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;
}
}
}
Answer by I_Am_Err00r · Jul 30, 2019 at 01:37 PM
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.
Thank you so much!!!
Worked like a charm after some understanding that I needed to be isGrounded once thePlayer touches the ground and can jump again. Thank you!!!!
This is not really a good approach. As long as isGrounded is false you queue another SecondChance execution every frame. while this is relatively irrelevant for a single jump, if can easily produce interference when you jump in quick succession or if you loose ground contact quite frequently. A re-triggerable cooldown would be simpler, more reliable and less resource intensive.
Answer by Bunny83 · Jul 31, 2019 at 06:12 PM
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.
Thank you for your time and effort to help!
I would agree to this because of your great explanation about doing things the "right" way resources-wise and due to its simplicity.
However, if I make that delay for jump a quarter of a second, I can squeeze 2 jumps if I am quick enough. Now - this is not my goal. I would like to keep a single jump only WHILE having that delay where player is falling down from platforms but still able to save themselves during that quarter of a second.
Is there any way to prevent any jumps after the initial one?
@tornprince2012 yes you can do that. The way to do it is also implement a max number of jumps. Basically you create a Public int maxJumps = 1; and Public int jumpCount;
At start/awake go jumpCount = maxJumps;
Then whenever grounded go jumpCount= max jumps;
Whenever you do a jump go jumpCount -= 1; Then add another if statement when checking if it's permissible to jump with If (jumpCount > 0)
Important to note, jump refresh should be tied to the ground detection, not the "second chance" timer, as tying it to the second chance timer will allow the same multi jump issue.
An alternative or additional approach would be to put a timer between jumps, this would also be good if you wanted your character to be able to double jump.
Right, either a maxJumpCount or simply set the timer to 0 when you actually jump. That timer just acts like a bool which allows you to jump as long as it's greater than 0. Setting it to 0 would immediately stop any additional jumps.
Your answer
![](https://koobas.hobune.stream/wayback/20220612205632im_/https://answers.unity.com/themes/thub/images/avi.jpg)