# The player can jump forever without approaching the ground

I'm making a 2D platformer with Unity. I want to support both keyboard and gamepad, so I'm using the new Input System and Player Input component with invoke unity events behaviour. I defined two variables called GroundedRememeber and JumpPressedRemember and basically they work like timers and check if the player leaves the ground and then the player can jump when he is near the ground without need to touch it. But the problem is that these timers are not working and the player can jump forever when I press jump button rapidly.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour, PlayerInputActions.IPlayerActions
{
private PlayerInputActions controls;
private Rigidbody2D rb;
private Animator anim;
private bool facingRight = true;
private Vector2 moveInput;
[SerializeField] private float jumpForce;
float JumpPressedRemember = 0;

[SerializeField] float JumpPressedRememberTime = 0.2f;
float GroundedRemember = 0;

[SerializeField]  float GroundedRememberTime = 0.25f;
[SerializeField]  float HorizontalAcceleration = 1;
[SerializeField] [Range(0, 1)] float HorizontalDampingBasic = 0.5f;
[SerializeField] [Range(0, 1)] float HorizontalDampingWhenStopping = 0.5f;
[SerializeField] [Range(0, 1)] float HorizontalDampingWhenTurning = 0.5f;
[SerializeField] [Range(0, 1)] float JumpHeight = 0.5f;

private void Awake()
{
controls = new PlayerInputActions();

controls.Player.SetCallbacks(this);
}
void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void PlayerInputActions.IPlayerActions.OnMove(InputAction.CallbackContext context)
{

}

void PlayerInputActions.IPlayerActions.OnJump(InputAction.CallbackContext context)
{
switch (context.phase)
{
case InputActionPhase.Performed:
this.Jump();
break;
}
}

void PlayerInputActions.IPlayerActions.OnShoot(InputAction.CallbackContext context)
{

}

public void Jump()
{

}

void FixedUpdate()
{

if(facingRight == false && moveInput.x > 0){

Flip();

}else if (facingRight == true && moveInput.x < 0){

Flip();

}
}
void Flip(){

facingRight = !facingRight;
Vector3 Scaler = transform.localScale;
Scaler.x *= -1;
transform.localScale = Scaler;

}

void OnEnable()
{
controls.Enable();
}

void OnDisable()
{
controls.Disable();
}
void Update()
{

Vector2 GroundedBoxCheckPosition = (Vector2)transform.position + new Vector2(0, -0.01f);
Vector2  GroundedBoxCheckScale = (Vector2)transform.localScale + new Vector2(-0.02f, 0);
bool Grounded = Physics2D.OverlapBox(GroundedBoxCheckPosition, transform.localScale, 0);

GroundedRemember -= Time.deltaTime;
if (Grounded)
{
GroundedRemember = GroundedRememberTime;
}

JumpPressedRemember -= Time.deltaTime;
if (controls.Player.Jump.triggered)
{
JumpPressedRemember = JumpPressedRememberTime;
}
if (controls.Player.Jump.triggered)
{
if (rb.velocity.y > 0)
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * JumpHeight);
}
}
if ((JumpPressedRemember > 0) && (GroundedRemember > 0))
{
JumpPressedRemember = 0;
GroundedRemember = 0;

rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
float HorizontalVelocity = rb.velocity.x;
HorizontalVelocity += moveInput.x;

if (Mathf.Abs(moveInput.x) < 0.01f)
HorizontalVelocity *= Mathf.Pow(1f - HorizontalDampingWhenStopping, Time.deltaTime * 10f);
else if (Mathf.Sign(moveInput.x) != Mathf.Sign(HorizontalVelocity))
HorizontalVelocity *= Mathf.Pow(1f - HorizontalDampingWhenTurning, Time.deltaTime * 10f);
else
HorizontalVelocity *= Mathf.Pow(1f - HorizontalDampingBasic, Time.deltaTime * 10f);

rb.velocity = new Vector2(HorizontalVelocity, rb.velocity.y);
}
}


• Okay but the main point of this code is not using ground check. I defined JumpPressedRemember and GroundedRemember and I expected them to work. Basically what they do is that when the player jumps and a certain time passed, he can jump again without actually being grounded but just being near ground. – Ali Oct 25 '20 at 15:45
• Did you try displaying the value of grounded somewhere to check that it's detecting ground and only ground? Your OverlapBox query looks like it would return true by detecting the player's own collider. – DMGregory Oct 25 '20 at 15:48
• No, I didn't try that but I tried Debug.Log of the grounded and it shows true even when the player is in the air. Could you please tell me to how to display the value of grounded because I'm not sure I understand it correctly. – Ali Oct 25 '20 at 16:52
• I believe your overlapbox that sets your grounded variable may be responding to the collider on the player itself? – Weckar E. Oct 30 '20 at 3:25
• Yes maybe you're right. What do you recommend? – Ali Oct 30 '20 at 13:34

As mentioned in the comments, it looks like this line...

bool Grounded = Physics2D.OverlapBox(GroundedBoxCheckPosition, transform.localScale, 0);


...is detecting an overlap with the player's own collider, and so it concludes that you are always "on the ground".

If you want this check to only detect "ground" objects, then you need to tell it that's what you want.

Add a new public variable to the top of your script like so:

[SerializeField] LayerMask groundLayers;


In the inspector, edit this field and check-off only those physics layers that you're using for objects the player can stand on.

Make sure the player characters themselves (and crucially, their collider(s)) are on a different layer.

Now edit your code like so:

bool Grounded = Physics2D.OverlapBox(
GroundedBoxCheckPosition, // Center of the box to check.
transform.localScale,     // Size of the box to check.
0,                        // Rotation angle of the box.
groundLayers              // Which physics layers to check.
);


The order is important here. LayerMask can convert implicitly to an int, and from int to float, so if you put it in the wrong argument you often won't get a compiler error, the engine will just misread your LayerMask as an angle or depth and carry on doing the wrong thing without warning. So be sure you've put it in the fourth slot.

In your latest code, you have an extra jump that's not controlled by your grounded check at all:

if (controls.Player.Jump.triggered) {
if (rb.velocity.y > 0) {
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * JumpHeight);
}
}


Anytime the player has even the slightest upward velocity, they'll be able to jump without ground nearby. Did you mean to replace this when you changed your code to use the Remembered variables?

• Thank you for answering my question. I did everything you said but the player can't even jump now. I'll attach some photos of the Inspector under the code. – Ali Nov 2 '20 at 21:49
• Sounds like your grounded check might never be detecting the ground now. Did you make sure your ground objects are using the Ground layer? Show us that. – DMGregory Nov 2 '20 at 21:54
• Also, changing "Order in Layer" didn't work for me. – Ali Nov 3 '20 at 14:45
• Order in layer has nothing to do with this. Did you read the edit I made above about the jump code that's missing a ground check? – DMGregory Nov 3 '20 at 15:21
• Yes, I'm reading it now. I wanted to replace that line by Remembered code but I forgot. So, Do you think I need to delete that line or I need to somehow add the ground check? – Ali Nov 3 '20 at 16:29