Jumping with adjustable force

Hi guys, I need a script to let my player jump based on how long a player pushes a button. This is what I have so far but it really doesn’t look natural. Keep in mind that the player is in a fixed position and the ground moves underneath him. Here is the script:

using UnityEngine;
using System.Collections;

public class CharacterMovementScript : MonoBehaviour {
	
	public bool keyPressed;
	public GameObject character; 
	public bool contactGround;
	private float maxHeight = 3f;
	private float jumpSpeed = 0.2f;
	
	void CollisionTest()
	{
		Vector3 direction = new Vector3(0,-1,0);
		if (Physics.Raycast(character.transform.position, direction))
		{
			contactGround = true;
			jumpSpeed = 0.2f;
		}else
		{
			contactGround = false;
		}
	}
	
	public void CharacterMovement()
	{	
		CollisionTest();
		
		if (Input.GetKeyUp ("space"))
		{
		keyPressed = false;
		}
		
		if (Input.GetKeyDown ("space") && contactGround)
		{
		keyPressed = true;
		}
		
		if(keyPressed)
		{
		jumpSpeed = jumpSpeed * 0.95f;
		character.transform.position += new Vector3(0,jumpSpeed,0);
		}
		
		if(character.transform.position.y > maxHeight)
		{
		keyPressed = false;
		}
		
	}

Thank you in advance!

Right, well there’s lots of different ways to do jumps, and it depends very much on the exact effect you’re after. It strikes me that what you are probably looking for here is:

  • If the user just ‘taps’ the key, the player jumps to some minimum height
  • If the user holds the key, the end jump height will get higher, up to some maximum value

So we end up really with 3 variables:

  • the minimum height
  • the maximum height
  • how long the player has to hold the jump key to achieve maximum height

Before worrying about achieving precise heights (as that requires some slightly awkward maths), lets replace the problem with:

  • an initial jump velocity
  • an initial acceleration
  • how long the player can hold the jump key for (before it stops having an effect)

So when a jump first begins we must do something like:

if (Input.GetKeyDown ("space") && contactGround)
{
    //set initial rigid body velocity
    curr_vel = rigidbody.velocity;
    curr_vel.y = initial_jump_velocity
    rigidbody.velocity = curr_vel

    //store that we are jumping, and record the time we started
    is_jumping = true
    jump_start_time = Time.time
}

With that code, I now have the player initially pinging into the air, and know that I am jumping, and when I started jumping. Next:

//if jumping, check if key is held down
if (is_jumping && Input.GetKey("space"))
{
    //still holding the key, so check if we're within our maximum key held time
    time_since_jump = Time.time - jump_start_time
    if(time_since_jump < max_jump_time)
    {
        //calculate a value that is 1 at the start of the jump, and 0 at max_jump_time
        float t = 1-time_since_jump / max_jump_time;

        //(we might at this stage do something like t = t*t, which would make the fall off a curve)

        //from that, calculate an amount we're allowed to accelerate the player by (which fades to 0)
        acceleration = max_acceleration * t;

        //apply acceleration
        curr_vel = rigidbody.velocity;
        curr_vel.y += acceleration;
        rigidbody.velocity = curr_vel;
    }
}

This is common pattern you’ll see across a lot of games. I first calculate how long it’s been since I started jumping. if I’m within a given time limit, I calculate a number (the interpolator, often named ‘t’) that is 1 at the start, and 0 at the end of my time limit.

Using the interpolator, I then calculate how much I can accelerate the player by. As I mention in the comments, it’s not unusual to square or use mathf.pow on the interpolator, which would make the acceleration fall off in different ways - one to experiment with to get the feel you want.

That alone should get you going. It doesn’t allow you to specify an exact max height, but by fiddling with the initial jump height and acceleration you should be able to tune it nicely.

If you really want to calculate precise values, then you’ll need to take it a step further, and calculate (based on gravity) the exact linear velocity required to acheive a given height. In this scenario, each frame we would know:

  • the difference between our current height and our target height
  • how much more time is left for us to reach the target height

You then have to plug it into the quadratic equation:

s = ut + 0.5at^2

where s is the change in height we need, a is acceleration (aka gravity), t is how much time we have left, and u is the value we’re interested in - how fast should we be going?

which gives us:

u = (s - 0.5at^2)/t

Each frame while the jump key was held, you’d gradually increase the target height, calculate the velocity required, and then apply it. I don’t quite have time to write the exact code for this, but to be honest, I’d just go for the first approach and tinker with it - start by tuning a nice and satisfying small ‘single tap’ jump, then fiddle with the acceleration (and potentially what you do with the interpolator) until the max jump feels good.