Search Unity

  1. We are migrating the Unity Forums to Unity Discussions by the end of July. Read our announcement for more information and let us know if you have any questions.
    Dismiss Notice
  2. Dismiss Notice

Question Jumping mechanics not working properly.

Discussion in '2D' started by Ehchdhqwagads, May 15, 2024.

  1. Ehchdhqwagads

    Ehchdhqwagads

    Joined:
    Mar 16, 2024
    Posts:
    5
    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
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5.  
    6. public class PlayerController : MonoBehaviour
    7. {
    8.     public float moveSpeed;
    9.     public float jumpForce;
    10.     public float groundDrag;
    11.     public float gravity;
    12.     private float horizontalInput;
    13.     public Rigidbody2D Rigidbody;
    14.     private BoxCollider2D Collider;
    15.     private bool onGround;
    16.     void Start()
    17.     {
    18.         Rigidbody = GetComponent<Rigidbody2D>();
    19.         Collider = GetComponent<BoxCollider2D>();
    20.         Physics2D.gravity = new Vector2(0, -gravity);
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.         horizontalInput = Input.GetAxis("Horizontal");
    26.         Rigidbody.AddForce(Vector2.right * Mathf.Clamp(moveSpeed * horizontalInput, -10, 10));
    27.         onGround = Physics2D.Raycast(transform.position, Vector2.down, Collider.size.y / 2 + 0.025f);
    28.         if (Input.GetKey(KeyCode.Space) && onGround)
    29.         {
    30.             Rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
    31.         }
    32.         if (onGround)
    33.         {
    34.             Rigidbody.drag = groundDrag;
    35.         }
    36.         else
    37.         {
    38.             Rigidbody.drag = 0;
    39.         }
    40.     }
    41. }
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,443
    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.
     
    Last edited: May 15, 2024
    dstears likes this.
  3. Ehchdhqwagads

    Ehchdhqwagads

    Joined:
    Mar 16, 2024
    Posts:
    5
    Your solution fixed the large jumps and left me with this new script:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5.  
    6. public class PlayerController : MonoBehaviour
    7. {
    8.     public float moveSpeed;
    9.     public float jumpForce;
    10.     public float groundDrag;
    11.     public float gravity;
    12.     private float horizontalInput;
    13.     private bool spaceDown;
    14.     public Rigidbody2D Rigidbody;
    15.     private BoxCollider2D Collider;
    16.     private bool onGround;
    17.     void Start()
    18.     {
    19.         Rigidbody = GetComponent<Rigidbody2D>();
    20.         Collider = GetComponent<BoxCollider2D>();
    21.         Physics2D.gravity *= gravity;
    22.     }
    23.  
    24.     void FixedUpdate()
    25.     {
    26.         onGround = Physics2D.Raycast(transform.position, Vector2.down, Collider.size.y / 2 + 0.025f);
    27.         if (spaceDown && onGround)
    28.         {
    29.             Rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
    30.         }
    31.         if (onGround)
    32.         {
    33.             Rigidbody.drag = groundDrag;
    34.         }
    35.         else
    36.         {
    37.             Rigidbody.drag = 0;
    38.         }
    39.     }
    40.  
    41.     void Update()
    42.     {
    43.         horizontalInput = Input.GetAxisRaw("Horizontal");
    44.         spaceDown = Input.GetKeyDown(KeyCode.Space);
    45.         Rigidbody.AddForce(Vector2.right * Mathf.Clamp(moveSpeed * horizontalInput, -10, 10));
    46.     }
    47. }
    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?
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,443
    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:
    Code (CSharp):
    1. void FixedUpdate()
    2. {
    3.     onGround = Physics2D.Raycast(transform.position, Vector2.down, Collider.size.y / 2 + 0.025f);
    4.     if (spaceDown && onGround)
    5.     {
    6.         Rigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
    7.         spaceDown = false;
    8.     }
    9.     if (onGround)
    10.     {
    11.         Rigidbody.drag = groundDrag;
    12.     }
    13.     else
    14.     {
    15.         Rigidbody.drag = 0;
    16.     }
    17. }
    18.  
    19. void Update()
    20. {
    21.     if (spaceDown == false && Input.GetKeyDown(KeyCode.Space))
    22.     {
    23.         spaceDown = true;
    24.     }
    25.    
    26.     horizontalInput = Input.GetAxisRaw("Horizontal");
    27.     Rigidbody.AddForce(Vector2.right * Mathf.Clamp(moveSpeed * horizontalInput, -10, 10));
    28. }
    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.