Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

2D Jump Physics/Coding Issue

Discussion in 'Physics' started by Pk0n, Sep 7, 2020.

  1. Pk0n

    Pk0n

    Joined:
    Oct 25, 2019
    Posts:
    6
    Hello

    Im currently working on a 2D platformer and trying to use the new Input System 1.0, here is the code I have right now as the PlayerController
    Code (CSharp):
    1. public class PlayerController : MonoBehaviour
    2. {
    3.     float speed = 10.0f;
    4.     public float jumpHeight;
    5.     private bool isGrounded;
    6.  
    7.     private Rigidbody2D _rigidBody2D;
    8.     public float checkRadius;
    9.     public Transform boxPos;
    10.     public LayerMask whatIsGround;
    11.  
    12.       private void Start()
    13.     {
    14.         _rigidBody2D = GetComponent<Rigidbody2D>();
    15.     }
    16.  
    17.         void FixedUpdate()
    18.     {
    19.         if (Keyboard.current.aKey.isPressed)
    20.         {
    21.             OnMoveLeft();
    22.         }
    23.  
    24.         if (Keyboard.current.dKey.isPressed)
    25.         {
    26.             OnMoveRight();
    27.         }
    28.  
    29.         if (Keyboard.current.wKey.wasPressedThisFrame && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround)))
    30.         {
    31.             OnJump();
    32.         }
    33.  
    34.     }
    35.  
    36.     private void OnMoveLeft()
    37.     {
    38.         transform.Translate(Vector3.left * speed * Time.deltaTime);
    39.     }
    40.     private void OnMoveRight()
    41.     {
    42.         transform.Translate(Vector3.right * speed * Time.deltaTime);
    43.     }
    44.     private void OnJump()
    45.     {
    46.         _rigidBody2D.AddForce(Vector2.up * jumpHeight);
    47.     }
    48.    
    49. }
    50.  
    The problem is I dont want the player to jump by "spamming" the jump key, but I cant seem to make it work, since the "isGrounded" bool is supposed to be "false" when the player is "mid-air"

    Sorry for my bad English, I hope my issue is clear and understandable. Thanks for your support!
     
  2. lightbug14

    lightbug14

    Joined:
    Feb 3, 2018
    Posts:
    446
    Hi, that is happening because FixedUpdate doesn't run always, like Update does. Therefore, sometimes it detects a "GetDown" (or "GetUp"), but not always (for "Get" inputs this is not a problem). I think "Keyboard.current.wKey.wasPressedThisFrame" is the problem.

    That said, inputs should always be processed using Update, at least if you are manually doing polling. So, you must separate input from action, you can do so by storing the action value in some bool variable, and then "consume" that action in FixedUpdate. After that remember to reset the variable to false.

    A classic example would be something like this (i'm using the old input manager, replace that for the new input system ....also i added "jump" and "jumpUp"):
    Code (CSharp):
    1. bool jumpDown= false;
    2. bool jump= false;
    3. bool jumpUp= false;
    4.  
    5. void Update()
    6. {
    7.       // "|=" --> Once the right side is true, then the left side is always true, unless you reset the value (see FixedUpdate).
    8.       jumpDown|= Input.GetButtonDown("Jump");
    9.       jump|= Input.GetButton("Jump");
    10.       jumpUp|= Input.GetButtonUp("Jump");
    11. }
    12.  
    13. void FixedUpdate()
    14. {
    15.       // "Consume" the jump action
    16.       if( jumpDown)
    17.             Jump();
    18.  
    19.        //Reset the inputs
    20.        jumpDown= false;
    21.        jump = false;
    22.        jumpUp = false;
    23. }
     
  3. Pk0n

    Pk0n

    Joined:
    Oct 25, 2019
    Posts:
    6
    Thank you for replying so fast, I think I didn´t implemented it the right way, i ended up with something like this:
    Code (CSharp):
    1.  
    2. float speed = 10.0f;
    3.     public float jumpHeight;
    4.     private bool isGrounded;
    5.  
    6.     bool jumpDown = false;
    7.     bool jump = false;
    8.     bool jumpUp = false;
    9.  
    10.     private Rigidbody2D _rigidBody2D;
    11.     public float checkRadius;
    12.     public Transform boxPos;
    13.     public LayerMask whatIsGround;
    14.  
    15.       private void Start()
    16.     {
    17.         _rigidBody2D = GetComponent<Rigidbody2D>();
    18.     }
    19.  
    20.         void Update()
    21.     {
    22.         if (Keyboard.current.aKey.isPressed)
    23.         {
    24.             OnMoveLeft();
    25.         }
    26.  
    27.         if (Keyboard.current.dKey.isPressed)
    28.         {
    29.             OnMoveRight();
    30.         }
    31.  
    32.         // if (Keyboard.current.wKey.wasPressedThisFrame && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround)))
    33.         {
    34.             OnJump();
    35.         }
    36.  
    37.         jumpDown |= Keyboard.current.wKey.wasPressedThisFrame;
    38.         jump |= Keyboard.current.wKey.isPressed;
    39.         jumpUp |= Keyboard.current.wKey.wasReleasedThisFrame;
    40.  
    41.     }
    42.  
    43.     void FixedUpdate()
    44.     {
    45.         if (jumpDown)
    46.         {
    47.             OnJump();
    48.         }
    49.  
    50.         jumpDown = false;
    51.         jump = false;
    52.         jumpUp = false;
    53.     }
    54.     }
    The issue here is that without even pressing the key, the game object went flying up, i dont really understand why, and sorry if this is too obvious. Thank you!
     
  4. Pk0n

    Pk0n

    Joined:
    Oct 25, 2019
    Posts:
    6
    Ok so this was very dumb, I just needed to comment the whole old section here:

    Code (CSharp):
    1.  // if (Keyboard.current.wKey.wasPressedThisFrame && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround)))
    2.         //{
    3.         //    OnJump();
    4.         //}
    but I am still able to jump mid-air or spam the key to "float" and avoid falling :/
     
  5. lightbug14

    lightbug14

    Joined:
    Feb 3, 2018
    Posts:
    446
    You need to define a grounded state, if you don't do that your character will be able to jump forever. Allow jumping only if the grounded state is true (assuming that is what you want). A ground check can be done is many ways: bottom trigger, raycast, sphereCast, contact angle, etc.

    Also, using Translate (or any transform manipulation) with a dynamic rigidbody will lead to problems. Let the physics do that for you, you can use forces or just changing the velocity (in FixedUpdate).

    Edit: Sorry i missed the isGrounded flag:D... i think you are not using it (?)
     
    Last edited: Sep 7, 2020
  6. Pk0n

    Pk0n

    Joined:
    Oct 25, 2019
    Posts:
    6
    Ok so I ended up with this whole code:
    Code (CSharp):
    1. float speed = 10.0f;
    2.     public float jumpHeight;
    3.     private bool isGrounded;
    4.  
    5.     bool jumpDown = false;
    6.     bool jump = false;
    7.     bool jumpUp = false;
    8.  
    9.     private Rigidbody2D _rigidBody2D;
    10.     public float checkRadius;
    11.     public Transform boxPos;
    12.     public LayerMask whatIsGround;
    13.  
    14.       private void Start()
    15.     {
    16.         _rigidBody2D = GetComponent<Rigidbody2D>();
    17.     }
    18.  
    19.         void Update()
    20.     {
    21.         if (Keyboard.current.aKey.isPressed)
    22.         {
    23.             OnMoveLeft();
    24.         }
    25.  
    26.         if (Keyboard.current.dKey.isPressed)
    27.         {
    28.             OnMoveRight();
    29.         }
    30.  
    31.         // if (Keyboard.current.wKey.wasPressedThisFrame && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround)))
    32.         //{
    33.         //    OnJump();
    34.         //}
    35.  
    36.         jumpDown |= Keyboard.current.wKey.wasPressedThisFrame && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround));
    37.         jump |= Keyboard.current.wKey.isPressed && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround));
    38.         jumpUp |= Keyboard.current.wKey.wasReleasedThisFrame && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround));
    39.  
    40.         if (jumpDown)
    41.         {
    42.             OnJump();
    43.          
    44.         }
    45.         else
    46.         {
    47.             isGrounded = false;
    48.         }
    49.  
    50.     }
    51.  
    52.  
    53.     void FixedUpdate()
    54.     {
    55.         jumpDown = false;
    56.         jump = false;
    57.         jumpUp = false;
    58.     }
    59.     private void OnMoveLeft()
    60.     {
    61.         transform.Translate(Vector3.left * speed * Time.deltaTime);
    62.     }
    63.     private void OnMoveRight()
    64.     {
    65.         transform.Translate(Vector3.right * speed * Time.deltaTime);
    66.     }
    67.     private void OnJump()
    68.     {
    69.         _rigidBody2D.AddForce(Vector2.up * jumpHeight);
    70.     }
    As you can see I am somewhat lost, cause now it works, i think the "isGrounded" bool works fine, but the jump height appears just random, I mean when I press the key it sometimes jumps at a barely noticeable height and sometimes its more than I want to... I´m thinking that using the Input Manager is a big mistake at least for a complete noob like me

    Edit: Also I didn´t wanted to change the Translate since I dont really know if using a rb2D.AddForce would be better, since I dont want it to look like a jump, but just plain sliding like it is right now.
     
  7. lightbug14

    lightbug14

    Joined:
    Feb 3, 2018
    Posts:
    446
    Well,

    1. You are mixing two very important and different things in one line:
    Code (CSharp):
    1. jumpDown |= Keyboard.current.wKey.wasPressedThisFrame && (isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround));
    the ground check needs to be done once, no need to do it over and over again every time you need to check for an input:
    Code (CSharp):
    1. bool isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround);
    2. The ground check does not depend on the key pressed, So if you are not jumping that does not mean grounded is false.
    Code (CSharp):
    1. if (jumpDown && isGrounded)
    2. {
    3.       Jump();    
    4. }
    5.  
    3. You are not moving your rigidbody in FixedUpdate, you need to get/update the input information from Update and apply it in FixedUpdate.
    Unfortunately changing the Transform of a dynamic rigidbody is not "optional" (you can still do very interesting tricks by changing the Transform). AddForce will be too much for now, since you'll need some sort of control system to get the end result you want (like a PID controller for example), especially is you don't want a floaty and unstable character. You can always change the velocity directly, for example you can do something like this (there is a better way using "Axis", i'm just using the Keyboard):
    Code (CSharp):
    1.  
    2.  
    3. float horizontal = 0f;
    4.  
    5.     void Update()
    6.     {
    7.         // You can do this in FixedUpdate, since this is a "get" input (you only care about the current state).
    8.         // however, i like to put inputs in update.
    9.         horizontal = 0f;
    10.         if (Keyboard.current.aKey.isPressed)
    11.               horizontal = -1f;
    12.         else if (Keyboard.current.dKey.isPressed)
    13.               horizontal = 1f;
    14.  
    15.        ...
    16.      }
    17.  
    18.      void FixedUpdate()
    19.      {
    20.             ...
    21.             Move( Vector2.right * horizontal * speed );
    22.       }
    23.  
    24.       void Move( Vector2 velocity )
    25.       {
    26.               _rigidBody2D .velocity = velocity;
    27.       }
    28.  
    29.  
    4. Probably not a big deal, note that i changed every "OnXXXX" for "XXXX". Usually OnXXXX means an event.

    5. The best thing (IMO) about the new input system are the actions. An action can be "Jump", or "Crouch", or "Move", etc. These actions are not related a particular input device, that's what their bindings are for. So, "Jump" can have a "South button" (Gamepad) binding, a "Space" key (Keyboard) binding, etc etc etc. By calling "Keyboard.current. ...." in your code you are missing this feature completely.
    The same goes for the AD keys, put those into a 1D Axis action, and the input system will give you a float value between -1 a 1 (or whatever range you want). Just like the old "Horizontal" axis worked.


    So, try to organize everything like this:
    Code (CSharp):
    1. void Update()
    2. {
    3.        //Get inputs
    4. }
    5.  
    6. void FixedUpdate()
    7. {
    8.        //Rigidbody + Physics only -----------
    9.           // Ground check
    10.  
    11.           // Horizontal Movement
    12.  
    13.           // Jump
    14.  
    15.  
    16.        //Reset inputs
    17.  
    18. }
    One last TIP, sometimes FU can be called multiple times, for example if your framerate is too low. If so, you can use a LateFixedUpdate (this is a coroutine that use a "WaitForFixedUpdate" yield instruction) to reset the input values.
     
  8. Pk0n

    Pk0n

    Joined:
    Oct 25, 2019
    Posts:
    6
    So just wanted to update the thread, i ended up with this code:
    Code (CSharp):
    1. public class PlayerController : MonoBehaviour
    2. {
    3.     float speed = 10.0f;
    4.     private float thrust = .3f;
    5.    
    6.     private bool isGrounded;
    7.     public float checkRadius;
    8.     public Transform boxPos;
    9.     public LayerMask whatIsGround;
    10.  
    11.     private float jumpTimeCounter;
    12.     public float jumpTime;
    13.     private bool isJumping;
    14.  
    15.     private Rigidbody2D _rigidBody2D;
    16.    
    17.     private void Start()
    18.     {
    19.         _rigidBody2D = GetComponent<Rigidbody2D>();
    20.     }
    21.  
    22.     void Update()
    23.     {
    24.         isGrounded = Physics2D.OverlapCircle(boxPos.position, checkRadius, whatIsGround);
    25.  
    26.         if (Keyboard.current.aKey.isPressed)
    27.         {
    28.             OnMoveLeft();
    29.         }
    30.                
    31.         if (Keyboard.current.dKey.isPressed)
    32.         {
    33.             OnMoveRight();
    34.         }
    35.  
    36.         if (isGrounded == true && Keyboard.current.wKey.wasPressedThisFrame)
    37.         {
    38.             isJumping = true;
    39.             jumpTimeCounter = jumpTime;
    40.             OnJump();
    41.         }
    42.         if(Keyboard.current.wKey.isPressed && isJumping == true)
    43.         {
    44.             if (jumpTimeCounter > 0)
    45.             {
    46.                 OnJump();
    47.                 jumpTimeCounter -= Time.deltaTime;
    48.             }
    49.             else
    50.             {
    51.                 isJumping = false;
    52.             }
    53.         }
    54.         if (Keyboard.current.wKey.wasReleasedThisFrame)
    55.         {
    56.             isJumping = false;
    57.         }
    58.  
    59.     }
    60.     private void OnMoveLeft()
    61.     {
    62.         transform.Translate(Vector3.left * speed * Time.deltaTime);
    63.     }
    64.     private void OnMoveRight()
    65.     {
    66.         transform.Translate(Vector3.right * speed * Time.deltaTime);
    67.     }
    68.    
    69.     private void OnJump()
    70.     {
    71.         _rigidBody2D.AddForce(transform.up * thrust, ForceMode2D.Impulse);
    72.     }
    73. }
    So far I think it gave me the results i need right now. But now my issue is I need to implement the 2nd player movement, but have no idea how, any suggestions? Do i need to copy this script and chage the keys for it? It´s for a local multiplayer game