0
\$\begingroup\$

I created a script that allows the player to double jump, which works like a charm.

The problem is if the player walks off a cliff, they will still be able to jump twice. But that isn't how double jump works, right?

Can someone tell me how to make it so that if the player falls down a cliff, they will be able to jump only once?

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

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rb;
    private Animator anim;

    private float movementInputDirection;

    public float movementSpeed = 10f;
    public float jumpForce = 16f;

    public int amountOfJumps = 1;
    private int amountOfJumpsLeft;

    private bool isFacingRight = true;
    private bool isWalking;
    private bool isGrounded;
    private bool canJump;

    public float groundCheckRadius;
    public LayerMask whatIsGround;

    public Transform groundCheck;

    void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
    }

    void Start()
    {
        amountOfJumpsLeft = amountOfJumps; 
    }

    void Update()
    {
        CheckInput();
        CheckMovementDirection();
        UpdateAnimations();
        CheckIfCanJump();
    }

    private void FixedUpdate()
    {
        ApplyMovement();
        CheckSurroundings();
    }

    private void CheckMovementDirection() //flips sprite
    {
        if (isFacingRight && movementInputDirection < 0)
        {
            Flip();
        }
        else if (!isFacingRight && movementInputDirection > 0)
        {
            Flip();
        }

        if(rb.velocity.x != 0)
        {
            isWalking = true;
        }
        else
        {
            isWalking = false;
        }
    }

    private void UpdateAnimations()
    {
        anim.SetBool("IsWalking", isWalking);
    }

    private void CheckInput()
    {
        movementInputDirection = Input.GetAxisRaw("Horizontal");

        if (Input.GetButtonDown("Jump"))
        {
            Jump();
        }
    }

    private void ApplyMovement() //player movement
    {
        rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y);
    }

    private void Flip()
    {
        isFacingRight = !isFacingRight;
        transform.Rotate(0f, 180f, 0f);
    }

    private void Jump()
    {
        if (canJump)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
            amountOfJumpsLeft--;
        }
    }

    private void CheckSurroundings()
    {
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
    }

    private void CheckIfCanJump()
    {
        if (isGrounded && rb.velocity.y < 0.01)
        {
            amountOfJumpsLeft = amountOfJumps;
        }

        if (amountOfJumpsLeft <= 0)
        {
            canJump = false;
        }
        else
        {
            canJump = true;
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Double jumps do work that way in games like Robot Unicorn Attack, interestingly enough. ;) \$\endgroup\$
    – DMGregory
    Mar 21 '20 at 6:18
  • \$\begingroup\$ Well I don't want it to work that way, cos that will count as a triple jump. So is there a way to fix that? Thanks for the reply though. \$\endgroup\$
    – Fungigamer
    Mar 21 '20 at 6:24
1
\$\begingroup\$

The trick here is to count air jumps separately from the initial ground jump. You always allow a jump from the ground, and you decrement your remaining air jumps only if you're not on the ground.

So your amountOfJumpsLeft variable becomes airJumpsRemaining, and you can do something like this:

// Call this when you want to jump - it will report back whether is was successful.
public bool TryJump() {
    if (isGrounded && rb.velocity.y < jumpSpeed) {
        // Your first jump from ground is always valid.
        Jump();
        return true;
    } else if (airJumpsRemaining > 0) {
        // Only air jumps decrease the count.
        airJumpsRemaining--;
        Jump();  // Alternatively, you can call a different air jump method here.
        return true;
    }
    return false;
}

// No conditionals or state management in Jump() itself anymore.
private void Jump() {
    // Just don't call your variable "force" if you're using it as a "speed".
    // That'll confuse someone down the line to think it's in Newtons, not m/s.
    rb.velocity = new Vector2(rb.velocity.x, jumpSpeed);
}

Then when you update your grounded state, you immediately restore your air jumps.

private void CheckSurroundings()
{
    isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);

    if(isGrounded)
        airJumpsRemaining = amountOfJumps - 1;
}
\$\endgroup\$
2
  • \$\begingroup\$ Sorry but the code doesn't work. It even makes the player can infinitely jump. Btw, I don't have a variable called jumpVelocity, do you mean jumpForce? or just zero. \$\endgroup\$
    – Fungigamer
    Mar 21 '20 at 7:19
  • 1
    \$\begingroup\$ Sounds like you didn't integrate the code correctly. Make sure you use every part, including reading the comment where I caution about naming a variable "force" when you're not using it as a force. Also ensure your ground check radius is small enough that it's not still picking up ground on the up or hangtimg phases of your jump. \$\endgroup\$
    – DMGregory
    Mar 21 '20 at 7:26
0
\$\begingroup\$

In your Jump method, why don't you first do a check for IsGrounded and adjust the number of jumps? Something like:

private void Jump()
{
    if (canJump)
    {
        if (!isGrounded)
        {
            amountOfJumpsLeft--;
        }
        rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        amountOfJumpsLeft--;
    }
}

Essentially, if you call Jump and you are not on the ground, it reduces the number by 1 before executing the jump, so then when you perform the jump and it removes 1 more you are at 0 after the first jump. If you are on the ground, if skips this and removes 1 only after the first jump. The only caveat to this is that if you are on the ground when you first jump, then your second jump will reduce amountOfJumpsLeft by 2... but if it doesn't matter that amountOfJumpsLeft can be < 0, then who cares?

EDIT: Alternatively, if you can't have amountOfJumpsLeft < 0, you can do the same thing but just modify canJump so the next time Jump is called it fails the check:

private void Jump()
{
    if (canJump)
    {
        if (!isGrounded)
        {
            canJump = false;
        }
        rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        amountOfJumpsLeft--;
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.