Need Character to Jump once until touching ground again

The code I have lets character jump only once when you press spacebar again it will never work. I need to let character jump and once touch ground can jump again. Every solution I have tried i found online just shoots my character to the moon for some reason. Thanks for any help you could be.

    bool mGrounded = true;
    void handleMovement()
    {
        if (movementPressed)
        {
            animator.SetBool("Walking", true);
        }

        if (!movementPressed)
        {
            animator.SetBool("Walking", false);
        }

        if (runPressed)
        {
            animator.SetBool("Running", true);
        }

        if (!movementPressed && !runPressed)
        {
            animator.SetBool("Running", false);
        }

        if (jumpPressed && mGrounded == true)
        {
            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
            animator.SetBool("Jumping", true);
            mGrounded = false;
        }

        playerVelocity.y += gravityValue * Time.deltaTime;
        controller.Move(playerVelocity * Time.deltaTime);

        if (!jumpPressed)
        {
            animator.SetBool("Jumping", false);
        }
    }

I am pretty sure I need to set mGrounded back to true, but every way I have tried shoots me to the moon or allows character to infinitely jump.

You need to set it back to true when you touch the ground again. In order to do that, you need a proper ground detection logic that controls the value. Do you have anything like that in any of your code?

Thank you so much for your response. It does and when I switch to that it lets me jump multiple times, but still it let me levitate if I hold down space bar. it is much better. here is the code.

using UnityEngine;
using UnityEngine.InputSystem;


[RequireComponent(typeof(CharacterController))]
public class PlayerController : MonoBehaviour
{
    [SerializeField] private InputActionReference movementControl;
    [SerializeField] private InputActionReference jumpControl;
    [SerializeField] private float playerSpeed = 2.0f;
    [SerializeField] private float jumpHeight = 1.0f;
    [SerializeField] private float gravityValue = -9.81f;
    [SerializeField] private float rotationSpeed = 4f;

    private CharacterController controller;
    private Vector3 playerVelocity;
    private bool groundedPlayer;
    private Transform cameraMainTransform;

    Animator animator;

    PlayerControls input;

    Vector2 currentMovement;
    bool movementPressed;
    bool runPressed;
    bool jumpPressed;


    void Awake()
    {
        input = new PlayerControls();
        input.CharacterControls.Movement.performed += ctx => {
            currentMovement = ctx.ReadValue<Vector2>();
            movementPressed = currentMovement.x != 0 || currentMovement.y != 0;
        };
        input.CharacterControls.Run.performed += ctx => runPressed = ctx.ReadValueAsButton();
        input.CharacterControls.Jump.performed += ctx => jumpPressed = ctx.ReadValueAsButton();
    }


    private void OnEnable()
    {
        movementControl.action.Enable();
        jumpControl.action.Enable();
        input.CharacterControls.Enable();
    }

    private void OnDisable()
    {
        movementControl.action.Disable();
        jumpControl.action.Disable();
        input.CharacterControls.Disable();
    }

    void Start()
    {
        controller = gameObject.GetComponent<CharacterController>();
        cameraMainTransform = Camera.main.transform;
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        handleMovement();
        groundedPlayer = controller.isGrounded;
        if (groundedPlayer && playerVelocity.y < 0)
        {
            playerVelocity.y = 0f;
        }

        Vector2 movement = movementControl.action.ReadValue<Vector2>();
        Vector3 move = new Vector3(movement.x, 0, movement.y);
        move = cameraMainTransform.forward * move.z + cameraMainTransform.right * move.x;
        move.y = 0f;
        controller.Move(move * Time.deltaTime * playerSpeed);

        if (movement != Vector2.zero)
        {
            float targetAngle = Mathf.Atan2(movement.x, movement.y) * Mathf.Rad2Deg + cameraMainTransform.eulerAngles.y;
            Quaternion rotation = Quaternion.Euler(0f, targetAngle, 0f);
            transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * rotationSpeed);
        }
    }

    void handleMovement()
    {
        if (movementPressed)
        {
            animator.SetBool("Walking", true);
        }

        if (!movementPressed)
        {
            animator.SetBool("Walking", false);
        }

        if (runPressed)
        {
            animator.SetBool("Running", true);
        }

        if (!movementPressed || !runPressed)
        {
            animator.SetBool("Running", false);
        }

        if (jumpPressed && groundedPlayer == true)
        {
            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
            animator.SetBool("Jumping", true);
            groundedPlayer = false;
        }

        playerVelocity.y += gravityValue * Time.deltaTime;
        controller.Move(playerVelocity * Time.deltaTime);

        if (!jumpPressed)
        {
            animator.SetBool("Jumping", false);
        }
    } 
}

I see, so that’s probably Unity’s built-in CharacterController? Unless the colliders of your player object are set up in an unfortunate way, that one should work reliably. Let’s assume you’ve set them up in a way that doesn’t lead to weird behaviour.

Besides the custom movement logic you added, it looks like that could actually work so the next thing to look at are your input actions. How did you configure the input actions?

If i understand what your asking I setup jump to space bar. New input system go it set as a pass through, button. then the binding is on the space bar. Yes that is the built in one. I tried it with the built in one and making one myself. how I got it set up now is the only way that keeps it close to the ground. The examples for like ray casting and oncollision examples I found online shoot me 1000’s of feet in the air.

Looks like you’re forgetting to set jumpPressed back to false after consuming it here?

        if (jumpPressed && mGrounded == true)
        {
            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
            animator.SetBool("Jumping", true);
            mGrounded = false;
        }
        if (jumpPressed && groundedPlayer == true)
        {
            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
            animator.SetBool("Jumping", true);
            groundedPlayer = false;
        }

the mGrounded was changed to groundedPlayer and that is what is detecting if player is suppose to be touching ground. jumpPressed is if you press the space bar. Is my logic messed up is why it’s not working? thanks for replying. Do ya’ll think it might have to do with groundedPlayer is getting set to true due to something else. Could it be something in the new input system? how does groundedPlayer even work is what I don’t understand is it suppose to be set up as a tag or something?

I probably would’ve set up the Jump Action as button type, and add the interaction type Pressed & Released.
With that, the action would be performed on both interaction types and the jump flag would be controlled entirely by the input system.

As for the question about the grounded flag: unless you modify in in other places as well, it should work just fine. There’s no need to set up a tag or anything afaik. After all it’s your own field, and the only thing you do here is updating it every frame with the value of the CC’s isGrounded value.

I switched it over, but now just got to figure how to make it jump more then once. I got him to jump how I want he just won’t jump more then once. I cleaned my code up so it is easier to read and work on.

using UnityEngine;
using UnityEngine.InputSystem;


[RequireComponent(typeof(CharacterController))]
public class PlayerController : MonoBehaviour
{
    [SerializeField] private InputActionReference movementControl;
    [SerializeField] private float playerSpeed = 2.0f;
    [SerializeField] private float jumpHeight = 1.0f;
    [SerializeField] private float gravityValue = -9.81f;
    [SerializeField] private float rotationSpeed = 4f;

    private CharacterController controller;
    private Vector3 playerVelocity;
    private Transform cameraMainTransform;
    public bool isGrounded;

    Animator animator;

    PlayerControls input;

    Vector2 currentMovement;
    bool movementPressed;
    bool runPressed;
    bool jumpPressed;


    void Awake()
    {
        inputContext();
    }


    private void OnEnable()
    {
        movementControl.action.Enable();
        input.CharacterControls.Enable();
    }

    private void OnDisable()
    {
        movementControl.action.Disable();
        input.CharacterControls.Disable();
    }

    void Start()
    {
        controller = gameObject.GetComponent<CharacterController>();
        cameraMainTransform = Camera.main.transform;
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        handleMovement();
        handleJump();
    }

    void inputContext()
    {
        input = new PlayerControls();
        input.CharacterControls.Movement.performed += ctx =>
        {
            currentMovement = ctx.ReadValue<Vector2>();
            movementPressed = currentMovement.x != 0 || currentMovement.y != 0;
        };
        input.CharacterControls.Run.performed += ctx => runPressed = ctx.ReadValueAsButton();
        input.CharacterControls.Jump.performed += ctx => jumpPressed = ctx.ReadValueAsButton();
    }

    void handleMovement()
    {
        Vector2 movement = movementControl.action.ReadValue<Vector2>();
        Vector3 move = new Vector3(movement.x, 0, movement.y);
        move = cameraMainTransform.forward * move.z + cameraMainTransform.right * move.x;
        move.y = 0f;
        controller.Move(move * Time.deltaTime * playerSpeed);

        if (movement != Vector2.zero)
        {
            float targetAngle = Mathf.Atan2(movement.x, movement.y) * Mathf.Rad2Deg + cameraMainTransform.eulerAngles.y;
            Quaternion rotation = Quaternion.Euler(0f, targetAngle, 0f);
            transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * rotationSpeed);
        }

        if (movementPressed)
        {
            animator.SetBool("Walking", true);
        }

        if (!movementPressed)
        {
            animator.SetBool("Walking", false);
        }

        if (runPressed)
        {
            animator.SetBool("Running", true);
        }

        if (!movementPressed || !runPressed)
        {
            animator.SetBool("Running", false);
        }
    }

    void handleJump()
    {
        if(jumpPressed && isGrounded)
        {
            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
            animator.SetBool("Jumping", true);
            isGrounded = false;
        }

        playerVelocity.y += gravityValue * Time.deltaTime;
        controller.Move(playerVelocity * Time.deltaTime);

        if (!jumpPressed)
        {
            animator.SetBool("Jumping", false);
        }
    }
}

Also with that code not only does she jump once, but if you hold it down she stays in the air.

3r3baq

You have removed the line which assigned the CC’s isGrounded to your own field… of course you cannot jump more than once now, because it never resets to true.
You either need to have the assignment again or - what I meant when I said get the value directly - use the controller’s isGrounded property directly without your additional field.

I can’t really tell why it stays up in the air. Can you debug the jumping part (attach the debugger or add Debug.Logs) and find out what happens when you keep the jump button pressed? Bases in the code you’ve posted it doesn’t really make sense.

Also, why does your rigid body apply gravity when you actually try to apply gravity in code?
If you need the RB, either turn off the gravity or just feed it a velocity / add an impulsive force once at the beginning of the jump.

The latter would probably be easier since it wouldn’t require to handle “falling” on your own.

In order to get this working I strongly recommend to isolate the issue. Make a new script that only deals with jumping so that all the noise is removed.

I really don’t have the answers I am new to this. I watched to tutorials this is how they did it. It is all I could find on doing it with the new input system. Thanks for your help. I don’t understand a lot of what your saying, just cuz I am new. I am trying I think I should probably not use the new input system I am guessing. Since I am beginner it seems a lot harder to get it going then the old one.