For my game, I have implemented a jump counter that counts how many times a player can jump before they have to return to the ground. The counter is supposed to reset once the player is grounded again. However, the counter resets every time I press the jump button, resulting in the character being able to triple jump instead of double jump like I expected. (If the character falls off a ledge before jumping, the counter behaves as expected.) I think what's happening is that my ground detection system switches the "isGrounded" variable to false when the character jumps, switches it to true a split second later, and back to false a split second after that. How do I counteract this? Here is the code for my player controller: Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.UI; public class PlayerBehavior : MonoBehaviour { float movementValue = 0f; [SerializeField] float maxSpeed = 10f; public Rigidbody2D rb; CapsuleCollider2D playerCollider; GroundCheck groundCheck; [SerializeField] float jumpForce = 125f; [SerializeField] float airControl = 5f; [SerializeField] float dodgeForce = 10f; [SerializeField] bool dodged = false; public float decceleration = 1f; bool isGrounded; bool isMoving; enum MovementCodeOptions { Default, Simple, Alternate }; [SerializeField] MovementCodeOptions movementCode = MovementCodeOptions.Default; bool isFacingRight = true; [SerializeField] int jumpCounter = 2; bool canMove = true; private void Awake() { rb = gameObject.GetComponent<Rigidbody2D>(); playerCollider = gameObject.GetComponent<CapsuleCollider2D>(); groundCheck = transform.Find("GroundCheck").GetComponent<GroundCheck>(); } private void FixedUpdate() { Vector3 debugEndPos = transform.position; if (groundCheck.isGrounded) { isGrounded = true; if (jumpCounter < 2) jumpCounter = 2; Debug.Log("Player is grounded"); } else { isGrounded = false; } if (movementValue != 0f && canMove) { rb.velocity = new Vector2(movementValue * maxSpeed, rb.velocity.y); } else { rb.velocity = new Vector2(Mathf.MoveTowards(rb.velocity.x, 0f, decceleration * Time.deltaTime), rb.velocity.y); } if (movementValue == 0) { dodged = false; } if (movementValue < 0 && isFacingRight) Flip(); else if (movementValue > 0 && !isFacingRight) Flip(); /*if (rb.velocity.magnitude < maxSpeed) { rb.AddForce(new Vector2(movementValue * acceleration * Time.deltaTime * 50f, rb.velocity.y)); }*/ } void OnMove(InputValue value) { movementValue = value.Get<float>(); } public void Flip() { isFacingRight = !isFacingRight; transform.localScale = new Vector3(transform.localScale.x * -1, transform.localScale.y, transform.localScale.z); } void OnJump() { if (jumpCounter > 0) { //rb.AddForce(new Vector2(rb.velocity.x, jumpForce), ForceMode2D.Impulse); //rb.velocity = new Vector2(rb.velocity.x, jumpForce); rb.velocity = Vector2.up * jumpForce; jumpCounter -= 1; } } void OnBlock() { canMove = !canMove; } void OnDodge() { if (!dodged) { if (movementValue == 0) { } else if (movementValue > 0) { rb.velocity = new Vector2(dodgeForce, 0f); } else { rb.velocity = new Vector2(-dodgeForce, 0f); } dodged = true; } } } And here is the code for my ground detection system: Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class GroundCheck : MonoBehaviour { public bool isGrounded; private void OnCollisionEnter2D(Collision2D collision) { isGrounded = collision != null && (((1 << collision.gameObject.layer) & LayerMask.GetMask("Ground")) != 0); } private void OnCollisionExit2D(Collision2D collision) { isGrounded = false; } }
Check this: https://docs.unity3d.com/Manual/ExecutionOrder.html Note how the OnCollision calls happen after the FixedUpdate It might be necessary to only consider yourself grounded if your velocity is not upwards.
Whoa: EDIT: why are you copying IsGrounded over... don't do that!! That's mass confusion, cats living with dogs, etc. Use the value in place over on your groundCheck instance, ideally in a read-only capacity, but if you MUST clear it upon jumping, make a method to clear it.
How do I do that? (Where am I doing it wrong?) EDIT: Before I made this post, by the way, I made it so that the jump counter resets every time the player's vertical velocity is zero, and it worked. Now, I just want to know where my initial mistake was and how to create your solution.
That might be just fine! Make sure you can still jump again if you're running on a ramp or uneven ground. My idea would basically be, instead of ALWAYS triggering Grounded when the flags are right and you hit something, ONLY do it if velocity is not positive (or not positive enough): Code (csharp): if (rb.velocity.y <= 0.01f) { IsGrounded = true; } Another way would be to implement a dwell timer, where you need to be on the ground for a short period of time before it considers you on the ground. It can get a little bit fiddly... I would not have a separate ground checker, since ground checking is so integral to the main controller behavior.