Jumping mechanics not working properly.

I have a jumping script which adds force vertically when space is pressed and the player is on the ground. But whenever I jump, it sends the player into the stratosphere and then they begin descending slower than a feather. Also, onGround is only being set as false when space is down. I’m new, and I tried using Physics2D.Raycast() (oh yeah, I forgot to mention this is 2D) rather than OnCollisionEnter2D() to detect collisions with the ground. I used some code from a tutorial too : D

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

public class PlayerController : MonoBehaviour
{
    public float moveSpeed;
    public float jumpForce;
    public float groundDrag;
    public float gravity;
    private float horizontalInput;
    public Rigidbody2D Rigidbody;
    private BoxCollider2D Collider;
    private bool onGround;
    void Start()
    {
        Rigidbody = GetComponent<Rigidbody2D>();
        Collider = GetComponent<BoxCollider2D>();
        Physics2D.gravity = new Vector2(0, -gravity);
    }

    void Update()
    {
        horizontalInput = Input.GetAxis("Horizontal");
        Rigidbody.AddForce(Vector2.right * Mathf.Clamp(moveSpeed * horizontalInput, -10, 10));
        onGround = Physics2D.Raycast(transform.position, Vector2.down, Collider.size.y / 2 + 0.025f);
        if (Input.GetKey(KeyCode.Space) && onGround)
        {
            Rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        }
        if (onGround)
        {
            Rigidbody.drag = groundDrag;
        }
        else
        {
            Rigidbody.drag = 0;
        }
    }
}

GetKey returns true for every frame the key is pressed. It’s pretty hard to hold something down for exactly one frame, too. As physics runs slower than the usual frame-rate, this is probably causing a bunch of AddForce calls to build up before physics updates, causing the uber-jumps.

Probably want to use GetKeyDown instead, so it only does so for one frame.

Extra note, physics should really only be handled in FixedUpdate, while input should be read in Update.

1 Like

Your solution fixed the large jumps and left me with this new script:

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

public class PlayerController : MonoBehaviour
{
    public float moveSpeed;
    public float jumpForce;
    public float groundDrag;
    public float gravity;
    private float horizontalInput;
    private bool spaceDown;
    public Rigidbody2D Rigidbody;
    private BoxCollider2D Collider;
    private bool onGround;
    void Start()
    {
        Rigidbody = GetComponent<Rigidbody2D>();
        Collider = GetComponent<BoxCollider2D>();
        Physics2D.gravity *= gravity;
    }

    void FixedUpdate()
    {
        onGround = Physics2D.Raycast(transform.position, Vector2.down, Collider.size.y / 2 + 0.025f);
        if (spaceDown && onGround)
        {
            Rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        }
        if (onGround)
        {
            Rigidbody.drag = groundDrag;
        }
        else
        {
            Rigidbody.drag = 0;
        }
    }

    void Update()
    {
        horizontalInput = Input.GetAxisRaw("Horizontal");
        spaceDown = Input.GetKeyDown(KeyCode.Space);
        Rigidbody.AddForce(Vector2.right * Mathf.Clamp(moveSpeed * horizontalInput, -10, 10));
    }
}

But now the player only jumps sometimes when space is pressed. I have to spam the spacebar for a solid 2 - 3 seconds to register an input. Also, I chose GetKey() because I wanted the player to be able to hold jump and immediately jump upon contact with the ground. Unless there’s something I’m missing or not understanding, Is there a solution that solves both or at least the first of these problems?

As mentioned Update runs more frequently than FixedUpdate. As you can assigning to spaceDown every frame, and Input.GetKeyDown returns true only for the one frame, then there’s a high chance that regular Update calls are happening and setting it back to false before Unity runs a FixedUpdate all (especially on higher frame-rates).

So you want to make it more of a ‘switch’ and only set it to true if it is already false, and reset it back to false after you have actually jumped:

void FixedUpdate()
{
    onGround = Physics2D.Raycast(transform.position, Vector2.down, Collider.size.y / 2 + 0.025f);
    if (spaceDown && onGround)
    {
        Rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        spaceDown = false;
    }
    if (onGround)
    {
        Rigidbody.drag = groundDrag;
    }
    else
    {
        Rigidbody.drag = 0;
    }
}

void Update()
{
    if (spaceDown == false && Input.GetKeyDown(KeyCode.Space))
    {
        spaceDown = true;
    }
   
    horizontalInput = Input.GetAxisRaw("Horizontal");
    Rigidbody.AddForce(Vector2.right * Mathf.Clamp(moveSpeed * horizontalInput, -10, 10));
}

To do that you would use GetKey() (alongside the other techniques I’ve mentioned), but you generally need a short ‘grace period’ where, after you have jumped, you ignore further input for a short amount of time. This will involve running a small timer that you increment after the player has jumped.