fbpx

Jetpack in Unity

Read Time:11 Minute, 42 Second

Jetpacks are awesome, no argument about that. Among other games using jetpacks, we can name Grand Theft Auto, Overwatch (Pharah), and even a mobile game called “Jetpack Joyride“.

Let’s add a jetpack to our game PlayerMovement script from previous guides:

Understanding Jetpack

Like we did in our jumping guide, let’s first understand what jetpack does.
A jetpack allows us to travel upward by adding force in an upward direction. Unity can help us do that too and in fact, we learned about it in the jumping guide.

Adding the Jetpack

As mentioned before, we are using the guide (and the script) from our previous guide but if you do not want to follow them, here is the code we have written up to this point.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{

    private float horizontalMovement;
    private float verticalMovement; 

   // A field editable from inside Unity with a default value of 5
    public float speed = 5.0f;

    // A field for the player's running speed
    public float runningSpeed = 15f;

    // A field for the player's jump force
    public float jumpForce = 5f;

    // A field for the player's speed multiplier while jumping
    public float airMultiplier;

    // How much will the player slide on the ground
    // The lower the value, the greater distance the user will slide
    public float drag;

    private Rigidbody rb;
    private bool isOnGround;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true;
    }

    // Update is called once per frame
    void Update()
    {
        // Shout a short raycast downward to check if the player is standing on something
        float gameObjectHeight = gameObject.transform.localScale.y;
        isOnGround = Physics.Raycast(transform.position, Vector3.down, gameObjectHeight);

        // This will detect forward and backward movement
        horizontalMovement = Input.GetAxisRaw("Horizontal");

        // This will detect sideways movement
        verticalMovement = Input.GetAxisRaw("Vertical");

        // Calculate the direction to move the player
        Vector3 movementDirection = transform.forward * verticalMovement + transform.right * horizontalMovement;

        // Apply drag only if on the ground
        if (isOnGround)
        {
            rb.drag = drag;
        }
        else
        {
            movementDirection *= airMultiplier;
            rb.drag = 0;
        }

        // If left shift is held down, use the runningSpeed as the speed
        if (Input.GetKey(KeyCode.LeftShift))
        {
            rb.AddForce(movementDirection * runningSpeed, ForceMode.Force);
        }
        // Else (left shift is not held down) use the normal speed
        else
        {
            // Move the player
            rb.AddForce(movementDirection * speed, ForceMode.Force);
        }

        // If space is pressed, add force in an upward direction (jump)
        if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
        {
            // Add force in an upward direction (jump)
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
    }
}

Before we add a force upward, let’s add a new field to the PlayerMovement class.

// A field for the player's jetpack forcepublic float jetpackForce = 1.75f;

Now let’s add the force. Change this:

// If space is pressed, add force in an upward direction (jump)
if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
{
    // Add force in an upward direction (jump)
    rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}

To this:

// If space is pressed, and the player is on the ground
if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
{
    // Add one-time force in an upward direction (jump)
    rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
// Else, if space is pressed, and the player is in the air
else if (Input.GetKey(KeyCode.Space) && !isOnGround)
{
    // Add continous force in an upward direction (jetpack)
    rb.AddForce(Vector3.up * jetpackForce, ForceMode.Force);
} 

Now, save the script, go back to Unity and enter play mode. Jump and hold the space key down. You are flying!

Adding an Altitude Limit to the Jetpack

Unless you have an open-world game, where the player can travel upward to infinity, we would like to limit the altitude the player can reach when using a jetpack.

Create a new field in the class which will be used to set the the altitude limit for the player.

// A field for the player's max altitude
public float maxAltitude = 15f;

Now, add the following code at the end of your Update method.

if (transform.position.y > maxAltitude)
{
    transform.position = new Vector3(transform.position.x, maxAltitude, transform.position.z);
}

Save the script, go back to Unity and enter play mode. You can see our player can travel upward but hits an invisible ceiling. To change the height of the ceiling, change the maxAltitude value in Unity. Since we made it a public variable, we do not need to go back to the script to edit its value.

Organizing Our Variables

Take a look at our PlayerMovement controller inside Unity (if you cannot find it, click on the player game object the script is attached to). It starts to look a bit messy and unorganized. Follow our guide on keeping Unity component fields organized to do so.

We have done some organizing in our script and here is the result code.

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

public class PlayerMovement : MonoBehaviour
{
    private float horizontalMovement;
    private float verticalMovement;

    [Header("Movement")]
    // A field editable from inside Unity with a default value of 5
    public float speed = 5.0f;

    // A field for the player's running speed
    public float runningSpeed = 15f;

    // How much will the player slide on the ground
    // The lower the value, the greater distance the user will slide
    public float drag;

    [Header("Jump")]
    // A field for the player's jump force
    public float jumpForce = 5f;

    // A field for the player's speed multiplier while jumping
    public float airMultiplier;

    [Header("Jetpack")]
    // A field for the player's jetpack force
    public float jetpackForce = 1.75f;

    // A field for the player's max altitude
    public float maxAltitude = 15f;

    private Rigidbody rb;
    private bool isOnGround;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true;
    }

    // Update is called once per frame
    void Update()
    {
        // Shout a short raycast downward to check if the player is standing on something
        float gameObjectHeight = gameObject.transform.localScale.y;

        isOnGround = Physics.Raycast(transform.position, Vector3.down, gameObjectHeight);

        // This will detect forward and backward movement
        horizontalMovement = Input.GetAxisRaw("Horizontal");

        // This will detect sideways movement
        verticalMovement = Input.GetAxisRaw("Vertical");

        // Calculate the direction to move the player
        Vector3 movementDirection = transform.forward * verticalMovement + transform.right * horizontalMovement;

        // Apply drag only if on the ground
        if (isOnGround)
        {
            rb.drag = drag;
        }
        else
        {
            movementDirection *= airMultiplier;
            rb.drag = 0;
        }
        // If left shift is held down, use the runningSpeed as the speed
        if (Input.GetKey(KeyCode.LeftShift))
        {
            rb.AddForce(movementDirection * runningSpeed, ForceMode.Force);
        }
        // Else (left shift is not held down) use the normal speed
        else
        {
            // Move the player
            rb.AddForce(movementDirection * speed, ForceMode.Force);
        }

        // If space is pressed, and the player is on the ground
        if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
        {
            // Add one-time force in an upward direction (jump)
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
        // Else, if space is pressed, and the player is in the air
        else if (Input.GetKey(KeyCode.Space) && !isOnGround)
        {
            // Add continous force in an upward direction (jetpack)
            rb.AddForce(Vector3.up * jetpackForce, ForceMode.Force);
        }

        if (transform.position.y > maxAltitude)
        {
            transform.position = new Vector3(transform.position.x, maxAltitude, transform.position.z);
        }
    }
}

Adding Fuel to the Jetpack

Jetpacks cannot run forever. Not yet. At least not in real life. So let’s add a fuel limit to our jetpack.
The fuel will start regenerating if the jetpack was not used for a specific amount of time.

First of all, Let’s start by adding a field to determine the maximum fuel capacity by adding a field to our Jetpack section on the component.

// A field for the jetpack's max fual capacity
public float maxFuel = 750f;

Let’s also add a private field in which we will store the amount of fuel left. Assuming we are giving the player a fully fueled tank, we will also set the amount of fuel left to the maximum fuel capacity inside the Start method.

Add the remaining fuel field:

// A field for the jetpack's remaining fuel
private float remainingFuel;

And we will also set the remaining fuel to equal the maximum fuel capacity and the start of the game.
Add the following code to the Start method.

remainingFuel = maxFuel;

Now we need to decrease the amount of fuel left when the player uses the jetpack.
To do that, change the following code:

// Else, if space is pressed, and the player is in the air
else if (Input.GetKey(KeyCode.Space) && !isOnGround)
{
    // Add continous force in an upward direction (jetpack)
    rb.AddForce(Vector3.up * jetpackForce, ForceMode.Force);
}

To:

// Else, if space is pressed, and the player is in the air
else if (Input.GetKey(KeyCode.Space) && !isOnGround)
{
    // Add continous force in an upward direction (jetpack)
    if (remainingFuel > 0)
    {
        rb.AddForce(Vector3.up * jetpackForce, ForceMode.Force);
        remainingFuel -= 1;
    }
}
Debug.Log(remainingFuel);

Notice the Debug.Log(remainingFuel); at the end of the code. This will allow us to see how much fuel we have left in Unity’s Console view.
Save the script, go back to Unity and enter play mode. Make sure to also open the Console view so you can see how much fuel you have left.
Once you use up all the fuel our jetpack has, you will no longer be able to fly which is a bummer…

Adding Fuel Regeneration

To add the fuel regeneration, we will need a couple of things:

  • A new field in which we will determine after how much time the fuel should start regenerating.
  • Adding a regeneration method.

If you reached this point, you already know how to add a field to a class.
Let’s add a field called “regenerateAfter”, give it a default value of 3 and place it under our Jetpack section.
Also, add a private field called “jetpackLastUsed”. This field will help us determine what was the last time the player used the jetpack.

// A field for the jetpack's regeneration delay
public float regenerateAfter = 3f;

// A field to store the last time the player used the jetpack
private float jetpackLastUsed;
// A function for regenerating jetpack's fuel
private void regenerateJetpackFuel()
{
    // If the time from when the player stopped using the jetpack + the time after
    // which the fuel should start generating has passed, regenerate the fuel until reaching
    // the max fuel capacity
    if (jetpackLastUsed + regenerateAfter <= Time.time && remainingFuel < maxFuel)
    {
        remainingFuel += 1;
    }
}

Then, call the method before the Debug.Log() we added earlier and your code should now look like this.

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

public class PlayerMovement : MonoBehaviour
{
    private float horizontalMovement;
    private float verticalMovement;

    [Header("Movement")]
    // A field editable from inside Unity with a default value of 5
    public float speed = 5.0f;

    // A field for the player's running speed
    public float runningSpeed = 15f;

    // How much will the player slide on the ground
    // The lower the value, the greater distance the user will slide
    public float drag;

    [Header("Jump")]
    // A field for the player's jump force
    public float jumpForce = 5f;

    // A field for the player's speed multiplier while jumping
    public float airMultiplier;

    [Header("Jetpack")]
    // A field for the player's jetpack force
    public float jetpackForce = 1.75f;

    // A field for the player's max altitude
    public float maxAltitude = 15f;

    // A field for the jetpack's max fual capacity
    public float maxFuel = 750f;

    // A field for the jetpack's regeneration delay
    public float regenerateAfter = 3f;

    // A field to store the last time the player used the jetpack
    private float jetpackLastUsed;

    // A field for the jetpack's remaining fuel
    private float remainingFuel;

    private Rigidbody rb;
    private bool isOnGround;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true;
        remainingFuel = maxFuel;
    }

    // Update is called once per frame
    void Update()
    {
        // Shout a short raycast downward to check if the player is standing on something
        float gameObjectHeight = gameObject.transform.localScale.y;

        isOnGround = Physics.Raycast(transform.position, Vector3.down, gameObjectHeight);

        // This will detect forward and backward movement
        horizontalMovement = Input.GetAxisRaw("Horizontal");

        // This will detect sideways movement
        verticalMovement = Input.GetAxisRaw("Vertical");

        // Calculate the direction to move the player
        Vector3 movementDirection = transform.forward * verticalMovement + transform.right * horizontalMovement;

        // Apply drag only if on the ground
        if (isOnGround)
        {
            rb.drag = drag;
        }
        else
        {
            movementDirection *= airMultiplier;
            rb.drag = 0;
        }
        // If left shift is held down, use the runningSpeed as the speed
        if (Input.GetKey(KeyCode.LeftShift))
        {
            rb.AddForce(movementDirection * runningSpeed, ForceMode.Force);
        }
        // Else (left shift is not held down) use the normal speed
        else
        {
            // Move the player
            rb.AddForce(movementDirection * speed, ForceMode.Force);
        }

        // If space is pressed, and the player is on the ground
        if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
        {
            // Add one-time force in an upward direction (jump)
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
        // Else, if space is pressed, and the player is in the air
        else if (Input.GetKey(KeyCode.Space) && !isOnGround)
        {
            // Add continous force in an upward direction (jetpack)
            if (remainingFuel > 0)
            {
                rb.AddForce(Vector3.up * jetpackForce, ForceMode.Force);
                remainingFuel -= 1;

                // Remember the last time the player used the jetpack
                jetpackLastUsed = Time.time;
            }
        }

        regenerateJetpackFuel();
        Debug.Log(remainingFuel);

        if (transform.position.y > maxAltitude)
        {
            transform.position = new Vector3(transform.position.x, maxAltitude, transform.position.z);
        }
    }

    // A function for regenerating jetpack's fuel
    private void regenerateJetpackFuel()
    {
        // If the time from when the player stopped using the jetpack + the time after
        // which the fuel should start generating has passed, regenerate the fuel until reaching
        // the max fuel capacity
        if (jetpackLastUsed + regenerateAfter <= Time.time && remainingFuel < maxFuel)
        {
            remainingFuel += 1;
        }
    }
}

Save the script, go back to Unity and enter play mode. Again, also open the Console view so you can see how much fuel is left. Try flying around and wait for the fuel to start regenerating (3 seconds in our case).
Also, try to fly again when your fuel has started generating but still did not regenerated completely. You will see the regeneration stops and continues again after the regeneration cooldown.

Recap

In this guide, we used the script from previous guides mentioned at the start of this guide to add a jetpack to the player, and this is what we did:

  • Added the ability for the player to fly up.
  • Added a max altitude limit.
  • Added fuel.
  • Added fuel regeneration method.

Let us know in the comments below if you want us to add a guide on how to display a fuel gauge in the game.

Leave a Reply

Your email address will not be published. Required fields are marked *