Jump higher when holding button

I’m making a 2D platformer, and I’d like to make it so that my player character jumps with a default height on jump key down, but jumps higher until a set limit if the jump key is held (just like is the case in most (2D) platformers).

The following are the parts from my player script that are related to jumping (I think I got everything, but please let me know if you spot that something’s missing):

private RigidBody2D playerRigidBody;  
private bool isGrounded;  
private bool jump;

private Transform[] groundPoints;  
private float groundRadius;  
private LayerMask whatIsGround;  
private float jumpForce;  

void Start()
{
	playerRigidBody = GetComponent<RigidBody2D>();
}

void Update()  
{  
	HandleInput();
}

void FixedUpdate()
{
	isGrounded = IsGrounded();
	ResetValues();
}

private void HandleInput()
{
	if (isGrounded && jump)
	{
		isGrounded = false;
		playerRigidBody.AddForce(Vector2.up * jumpForce);
	}
}

private bool IsGrounded()
{
	if (playerRigidBody.velocity <= 0)
	{
		foreach (Transform point in groundPoints)
		{
			Collider2D[] colliders = Physics2D.OverlapCircleAll(point.position, groundRadius, whatIsGround);
			for (int i = 0; i < colliders.Length; i++)
			{
				if (colliders*.gameObject != gameObject)*
  •  		{*
    
  •  			return true;*
    
  •  		}*
    
  •  	}*
    
  •  }*
    
  • }*
  • return false;*
    }

private void ResetValues()
{

  • jump = false;*
    }
    I’m pretty noob still, both regarding Unity and regarding C#, so the code I have is more or less copied from tutorials on YouTube. I understand most of it, but I wouldn’t yet be able to write it myself from scratch. Just so you have an idea how pedagogical you need to be with me :slight_smile:
    Any ideas for what code I should add to my script to obtain the functionality I’m looking for?
    Thanks!

There are multiple ways to look at this problem.

From one angle, there’s the approach of adding a continuous force the longer you want to stay in the air.

However, another option is, instead, to perform a full-height jump UNLESS the jump button is released.

To give an example of this, the jumping force required must be calculated. This is a fairly straightforward calculation:

public static float CalculateJumpForce(float gravityStrength, float jumpHeight)
{
	//h = v^2/2g
	//2gh = v^2
	//sqrt(2gh) = v
	return Mathf.Sqrt(2 * gravityStrength * jumpHeight);
}

As an example of implementation (using a jump height of 5 units as an example), it could be called like this:

// Calculate the jumping force
jumpForce = CalculateJumpForce(Physics2D.gravity.magnitude, 5.0f);

Now, your maximum jumping height is set in stone. To utilize it, in this case, you can simply perform apply a downward force whenever the jump key isn’t held. To provide a simplified example of that:

// Basic variables required for this approach
bool isGrounded;
bool isJumping;
bool jumpKeyHeld;
Vector2 counterJumpForce;

// In Update()
if(Input.GetButtonDown("Jump"))
{
	jumpKeyHeld = true;
	if(isGrounded)
	{
		isJumping = true;
		playerRigidBody.AddForce(Vector2.up * jumpForce * playerRigidBody.mass, ForceMode2D.Impulse);
	}
}
else if(Input.GetButtonUp("Jump"))
{
	jumpKeyHeld = false;
}

// In FixedUpdate()
if(isJumping)
{
	if(!jumpKeyHeld && Vector2.Dot(playerRigidBody.velocity, Vector2.up) > 0)
	{
		playerRigidBody.AddForce(counterJumpForce * playerRigidBody.mass);
	}
}

Now, to explain what all of this is doing…

You’ll want to separate the state of “jumping” from the state of the button used to jump. This will give you the option of keeping the player aloft even when they didn’t specifically jump (That said, that’s certainly customizable).

First, get the input, as usual. Pressing the key can do a lot, while releasing the key doesn’t need to do much. When applying jumping force, however, you’ll want to ensure that the intended jump height is absolute. I’ve combined the direction (Vector2.up), the force of the jump (jumpForce), negation of mass (playerRigidBody.mass), as well as a ForceMode (ForceMode2D.Impulse) to ensure that it’s applied as a single application of force to jump to the proper height.

Then, I added a condition for when the jump is in progress. If you’re not holding the jump button (i.e. jumpKeyHeld) and are actively moving upward, an additional force will gradually push against you (specified by counterJumpForce). This way, holding the button won’t affect your jump to the intended maximum height, but it can instead be cut short by releasing the button. Additionally, by checking that the player’s moving upward, they won’t start rocketing toward the ground more quickly just because they’re not holding the jump button. It stops them from moving upward, but doesn’t force them down faster.

Add a condition to HandleInput:

else if(!isGrounded && <pressing jump button>)
{
  // Adds force until a limit
  // Caution to not add too much force and start flying like Goku
  // Measure time with Time.deltaTime to limit the amount of force you can add
}

Thanks for your suggestions!
My problem with the function was mostly how to not add the total force at once, but to instead stretch it over time, but of course that was because I forgot the function is repeated every frame, so I just had to add a little bit of force each frame.
I limited the total amount of force with a maxJump variable, but it might be a better idea to do it with a DeltaTime limit instead, since the function is in Update(), right? I mean, to avoid that the jump gets different realtime on different computers…

Hi @MeronSoda!
You need use some like this

 public float StandartJumpForce;  
 public float MaxJumpForce;  
 private float jumpForce; 

 void Update()  
 {  
        if (Input.GetKeyDown(KeyCode.Space))
        {
             jumpForce = MaxJumpForce;
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
             jumpForce = StandartJumpForce;
        }
        ClampVelocity() or ClampVelocityY ();              
}

there are several ways to limit rigidbody velocity.
you can limit velocity only Y

public void ClampVelocityY()
{
          float maxVelocityY = 10;
          Vector2 velocity =  playerRigidBody.velocity;
          velocity.y = Mathf.Clamp(velocity.y, Mathf.NegativeInfinity, maxVelocityY )
          playerRigidBody.velocity = velocity;
}

or clamp velocity magnitude

public void ClampVelocity()
{
          float maxVelocity = 10;
          playerRigidBody.velocity =  Vector2.ClampMagnitude(playerRigidBody.velocity, maxVelocity);
}

I don’t recommend move rigidbody by impulse (rigidbody.AddForce). More correctly use rigidbody.velocity, it get you more control of rigidbody

Several ways to do this.
Largely depends on your game and the way your jump is designed.

Now then, one way to solve this is to have a jump force and a decay rate for the force. Maintaining a max jump height can be tricky, so why not let the physics engine handle that for you?

Something along these lines should do the trick:

IEnumerator DoJump()
{
     //the initial jump
     playerRigidBody.AddForce(Vector2.Up * jumpForce);

     yield return null;

     //can be any value, maybe this is a start ascending force, up to you
     float currentForce = jumpForce;
     
     while(Input.GetKey(KeyCode.Space) && currentForce > 0)
     {
           playerRigidBody.AddForce(Vector2.Up * currentForce);
            
           currentForce -= decayRate * Time.deltaTime;

           yield return null;
     }
}

void Update()
{
     if(Input.GetKeyDown(KeyCode.Space) && isGrounded)
     {
           StartCoRoutine(DoJump());
     }
}

Time for a breakdown.
This approach uses a coroutine to keep applying a force to the rigidbody, as long as you hold the button down and have force to apply. The force will decay over a period of time, based on the decayRate variable. A larger decay rate means that you don’t fly as high. This way you don’t have to worry about height checks, the force will gradually taper off as you get higher and gravity will do the rest. Bare in mind, the jumpForce and decayRate will need to be modified to get the feel that you’re after.

As I said at the start, several ideas to choose from. Pick the one which makes the most sense for your game, and yourself.

Hopefully this helps.