How To Make A Rocket Jump Work In Unity 2D

I’m currently working on a personal project and I need a rocket jump to work. I’m having trouble in that I can’t get the rocket jump to send my player in a diagonal or sideways direction, it only sends them upwards. Here’s the code that controls how the explosions work:

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

public class Rocket : MonoBehaviour
{
    [SerializeField] private float rocketTime;
    [SerializeField] private float explosionRadius;
    [SerializeField] private float explosionForce;

    private void Start()
    {
        // Destroy the rocket after a set time if it doesn't collide
        Destroy(gameObject, rocketTime);
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        // Apply explosion logic and destroy the rocket
        ApplyExplosion();
        Destroy(gameObject);
    }

    private void ApplyExplosion()
    {
        // Check for collision with the player within the explosion radius
        Collider2D playerCollider = Physics2D.OverlapCircle(transform.position, explosionRadius, LayerMask.GetMask("Player"));

        if (playerCollider != null && playerCollider.CompareTag("Player"))
        {
            Rigidbody2D rb = playerCollider.GetComponent<Rigidbody2D>();
            PlayerMovement playerMovement = playerCollider.GetComponent<PlayerMovement>();

            if (rb != null && playerMovement != null)
            {
                // Calculate the direction from the explosion to the player
                Vector2 direction = playerCollider.transform.position - transform.position;

                // Calculate the distance between the explosion and the player
                float distance = direction.magnitude;

                // Apply force based on the square root of the distance (multiplied by a constant)
                float force = Mathf.Sqrt(distance) * explosionForce;

                // Normalize the direction and apply the force
                direction.Normalize();
                rb.AddForce(direction * force, ForceMode2D.Impulse);

                // Set isRocketJumping to true if the player is in the air
                if (!playerMovement.IsGrounded)
                {
                    playerMovement.SetRocketJumping(true);
                }
            }
        }
    }

    private void OnDrawGizmosSelected()
    {
        // Visualize the explosion radius in the editor
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, explosionRadius);
    }
}

Here’s some more code that further shows what I’m trying to achieve:


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



public class PlayerMovement : MonoBehaviour
{
    [Tooltip("Controls movement speed of the player")] 
    [SerializeField] private float runSpeed;

    [Tooltip("Controls force applied when the player jumps")]
    [SerializeField] private float jumpForce;

    [Tooltip("Adjustable distance used to check if the player is grounded")]
    [SerializeField] private float groundCheckDistance;

    [Tooltip("Select which layer is treated as the Ground layer")]
    [SerializeField] private LayerMask groundLayer;

    [Tooltip("Controls how far the player will slide when they come to a stop")]
    [SerializeField] private float friction;

    [Tooltip("Value to track the current velocity of the smooth damp")]
    [SerializeField] private float velocityX;

    [Tooltip("Controls how much the player can control movement in the air")]
    [SerializeField] private float airControlFactor;

    [Tooltip("Controls how much time the player has left to jump after leaving the ground")]
    [SerializeField] private float coyoteTime;

    [Tooltip("Controls how much time the player has to queue a jump before they land")]
    [SerializeField] private float jumpBufferTime;

     
    public bool IsGrounded => isGrounded;
    private bool isRocketJumping = false;
    private float jumpBufferCounter;
    private float coyoteTimeCounter;
    private Rigidbody2D rb;
    private bool isGrounded;


    private void Start()
    { rb = GetComponent<Rigidbody2D>(); }

    private void Update()
    {
        Move();
        Jump();
        GroundCheck();
    }

    /// <summary>
    /// This method allows for air control while the player isn't grounded and by fluidly reducing the movement to 0 using SmoothDamp, it simulates friction.
    /// </summary>
    private void Move()
    {
        float moveInput = Input.GetAxisRaw("Horizontal");

        // Adjust air control factor if rocket jumping
        float currentAirControlFactor = isRocketJumping ? airControlFactor * 1.5f : airControlFactor;

        // Reduces the player's movement by the air control factor when the player isn't grounded
        if (!isGrounded)
        { moveInput *= currentAirControlFactor; }

        if (moveInput == 0 && isGrounded)
        {
            float targetVelocityX = Mathf.SmoothDamp(rb.velocity.x, 0, ref velocityX, friction);

            if (Mathf.Abs(targetVelocityX) < 0.01f)
            { targetVelocityX = 0f; }
            rb.velocity = new Vector2(targetVelocityX, rb.velocity.y);
        }
        else
        {
            rb.velocity = new Vector2(moveInput * runSpeed, rb.velocity.y);
            velocityX = rb.velocity.x;
        }
    }


    public void SetRocketJumping(bool state)
    { isRocketJumping = state; }


    /// <summary>
    /// This method allows the player to have some time to jump after leaving the ground and allows for them to queue their jumps. 
    /// It also allows the player to jump higher based on how long the jump button is held for.
    /// </summary>
    private void Jump()
    {
        if (isGrounded)
        { coyoteTimeCounter = coyoteTime;}
        else
        { coyoteTimeCounter -= Time.deltaTime; }

        // Queues a jump after the button is pressed otherwise the buffer time is reduced when no jump input is given
        if (Input.GetButtonDown("Jump"))
        { jumpBufferCounter = jumpBufferTime; } 
        else
        { jumpBufferCounter -= Time.deltaTime; }
        
        
        if (coyoteTimeCounter > 0f && jumpBufferCounter > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
            jumpBufferCounter = 0f;
            coyoteTimeCounter = 0f;
        }

        // If the jump button is released while jumping, the vertical jump is halved which gives the player more control over jumps
        if (Input.GetButtonUp("Jump") && rb.velocity.y > 0f)
        { rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f); }  
        

    }


    /// <summary>
    /// This method uses a raycast to check if the player is grounded by casting a ray downwards from the player's position.
    /// If the ray hits what ever is set as the ground layer, then the boolean isGrounded is set to True.
    /// </summary>
    private void GroundCheck()
    {
        Vector2 position = transform.position;
        Vector2 direction = Vector2.down;
        float distance = groundCheckDistance;
        RaycastHit2D hit = Physics2D.Raycast(position, direction, distance, groundLayer);
        isGrounded = hit.collider != null;

        // Reset isRocketJumping when the player lands
        if (isGrounded)
        { isRocketJumping = false; }
        Debug.DrawRay(transform.position, Vector2.down * groundCheckDistance, Color.blue);
    }



    /// <summary>
    /// Draws blue line which is just used as a visual aid for the GroundCheck method.
    /// </summary>
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.blue;
        Gizmos.DrawLine(transform.position, transform.position + Vector3.down * groundCheckDistance);
    }
}

Here’s the last bit:

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

public class GunJump : MonoBehaviour
{
    [SerializeField] private Transform laserOrigin;
    [SerializeField] private LineRenderer lineRenderer;
    [SerializeField] private float maxLaserDistance;
    [SerializeField] private LayerMask layerMask;
    [SerializeField] private GameObject rocketPrefab;
    [SerializeField] private float rocketSpeed;

    private Camera mainCam;
    private Vector3 mousePos;

    private void Start()
    {
        mainCam = Camera.main;
        SetAimLineColor(this.GetComponent<LineRenderer>(), Color.white);
    }

    private void Update()
    {
        AimLine();

        if (Input.GetMouseButtonDown(0))
        { FireRocket(); }
    }

    private void AimLine()
    {
        mousePos = mainCam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, laserOrigin.position.z - mainCam.transform.position.z));
        Vector3 direction = (mousePos - laserOrigin.position).normalized;
        float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        transform.rotation = Quaternion.Euler(0f, 0f, rotZ);
        lineRenderer.SetPosition(0, laserOrigin.position);

        RaycastHit2D hit = Physics2D.Raycast(laserOrigin.position, direction, maxLaserDistance, layerMask);

        if (hit.collider != null)
        { lineRenderer.SetPosition(1, hit.point); }
        else
        { lineRenderer.SetPosition(1, laserOrigin.position + direction * maxLaserDistance); }
    }

    private void FireRocket()
    {       
        Vector2 direction = (mousePos - laserOrigin.position).normalized;      
        Vector3 spawnPosition = laserOrigin.position + (Vector3)direction * 1f; 
        GameObject rocket = Instantiate(rocketPrefab, spawnPosition, Quaternion.identity);

        
        Rigidbody2D rocketrb = rocket.GetComponent<Rigidbody2D>();
        if (rocketrb != null)
        { rocketrb.velocity = direction * rocketSpeed; }
    }


    private void SetAimLineColor(LineRenderer lineRendererToChange, Color newColor)
    {
        Gradient tempGradient = new Gradient();
        GradientColorKey[] tempColorKeys = new GradientColorKey[2];
        tempColorKeys[0] = new GradientColorKey(newColor, 0);
        tempColorKeys[1] = new GradientColorKey(newColor, 1);

        tempGradient.colorKeys = tempColorKeys;
        lineRendererToChange.colorGradient = tempGradient;
    }
}

Any horizontal velocity change from the rocket jump is being overwritten by lines 76 and 80 in the PlayerMovement script. Instead of setting rb.velocity you should try to use AddForce to move the player’s character around. And do it from within FixedUpdate.

1 Like

Thank you that fixed the issue entirely! I redid the Move function and now I can rocket jump in any direction.