0
\$\begingroup\$

I'm making a 2D platformer with Unity. I used this tutorial to write the code but with some changes like using the new Input System. Like the tutorial, I've defined three variables called GroundedRememeber, JumpPressedRemember and JumpPressedRememberTime and basically they work like timers and check if the player leaves the ground and if so, then the player can jump when it is near the ground without the need to touch it. The problem is that these timers are not working and the player can't jump, just clings to the ground. My player controller has no errors but the jumping is not working.

Here's my Player Controller script:

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

public class PlayerController : MonoBehaviour, PlayerInputActions.IPlayerActions
{
   private PlayerInputActions controls; 
   [SerializeField] LayerMask groundLayers;     
   private Rigidbody2D rb;
   private Animator anim;
   private bool facingRight = true;
   private Vector2 moveInput;
   private Vector2 playerSize;
   private float JumpPressedRemember = 0;
   
   [SerializeField] float JumpPressedRememberTime = 0.2f;
   private float GroundedRemember = 0;
     
   [SerializeField]  float GroundedRememberTime = 0.25f;  
   [SerializeField]  float HorizontalAcceleration = 1;
   [SerializeField] [Range(0, 1)] float HorizontalDampingBasic = 0.5f;
   [SerializeField] [Range(0, 1)] float HorizontalDampingWhenStopping = 0.5f;
   [SerializeField] [Range(0, 1)] float HorizontalDampingWhenTurning = 0.5f;
   [SerializeField] [Range(0, 10)] float JumpHeight = 5f;

   private void Awake() 
   {
      controls = new PlayerInputActions();    
      controls.Player.SetCallbacks(this);
      controls.Enable();
   }
   void Start()
   {
      rb = GetComponent<Rigidbody2D>();
      anim = GetComponent<Animator>();    
      playerSize = (Vector2) GetComponent<CapsuleCollider2D>().bounds.size + new Vector2(-0.02f, 0);   
   }
   void PlayerInputActions.IPlayerActions.OnMove(InputAction.CallbackContext context)
   {
      moveInput = context.ReadValue<Vector2>();
          
   }

   void PlayerInputActions.IPlayerActions.OnJump(InputAction.CallbackContext context) {
       switch (context.phase) {
            case InputActionPhase.Performed:
            JumpPressedRemember = JumpPressedRememberTime;
               break;
       }
   }
      
   void FixedUpdate()
   {    
       if(facingRight == false && moveInput.x > 0){       
           Flip();
       } else if (facingRight == true && moveInput.x < 0){        
           Flip();    
       }
   }
   void Flip(){
        facingRight = !facingRight;
        Vector3 Scaler = transform.localScale;
        Scaler.x *= -1;
        transform.localScale = Scaler;
    
   void Update()
   { 
       Vector2 GroundedBoxCheckPosition = (Vector2)transform.position + new Vector2(0, -0.01f);
       bool Grounded = Physics2D.OverlapBox(GroundedBoxCheckPosition, playerSize, 0, groundLayers);
    
       GroundedRemember -= Time.deltaTime;
       if (Grounded)
       {
           GroundedRemember = GroundedRememberTime;
       }

       JumpPressedRemember -= Time.deltaTime; 
  
       if ((JumpPressedRemember > 0) && (GroundedRemember > 0)) {
            rb.AddForce(Vector2.up * JumpHeight, ForceMode2D.Impulse);
            GroundedRemember = 0;
            JumpPressedRemember = 0;  
       }
    
       float HorizontalVelocity = rb.velocity.x;
       HorizontalVelocity += moveInput.x;
          
       if (Mathf.Abs(moveInput.x) < 0.01f)
            HorizontalVelocity *= Mathf.Pow(1f - HorizontalDampingWhenStopping, Time.deltaTime * 10f);
       else if (Mathf.Sign(moveInput.x) != Mathf.Sign(HorizontalVelocity))
           HorizontalVelocity *= Mathf.Pow(1f - HorizontalDampingWhenTurning, Time.deltaTime * 10f);
       else
            HorizontalVelocity *= Mathf.Pow(1f - HorizontalDampingBasic, Time.deltaTime * 10f);

       rb.velocity = new Vector2(HorizontalVelocity, rb.velocity.y);    
   }
}

This is the original code from the tutorial which is using the old Input System:

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

public class PlayerMovement : MonoBehaviour
{
    [SerializeField]
    LayerMask lmWalls;
    
    [SerializeField]
    float fJumpVelocity = 5;

    Rigidbody2D rigid;

    float fJumpPressedRemember = 0;
    [SerializeField]
    float fJumpPressedRememberTime = 0.2f;

    float fGroundedRemember = 0;
    [SerializeField]
    float fGroundedRememberTime = 0.25f;

    [SerializeField]
    float fHorizontalAcceleration = 1;
    [SerializeField]
    [Range(0, 1)]
    float fHorizontalDampingBasic = 0.5f;
    [SerializeField]
    [Range(0, 1)]
    float fHorizontalDampingWhenStopping = 0.5f;
    [SerializeField]
    [Range(0, 1)]
    float fHorizontalDampingWhenTurning = 0.5f;

    [SerializeField]
    [Range(0, 1)]
    float fCutJumpHeight = 0.5f;

    void Start ()
    {
        rigid = GetComponent<Rigidbody2D>();
    }
    
    void Update ()
    {
        Vector2 v2GroundedBoxCheckPosition = (Vector2)transform.position + new Vector2(0, -0.01f);
        Vector2 v2GroundedBoxCheckScale = (Vector2)transform.localScale + new Vector2(-0.02f, 0);
        bool bGrounded = Physics2D.OverlapBox(v2GroundedBoxCheckPosition, v2GroundedBoxCheckScale, 0, lmWalls);

        fGroundedRemember -= Time.deltaTime;
        if (bGrounded)
        {
            fGroundedRemember = fGroundedRememberTime;
        }

        fJumpPressedRemember -= Time.deltaTime;
        if (Input.GetButtonDown("Jump"))
        {
            fJumpPressedRemember = fJumpPressedRememberTime;
        }

        if (Input.GetButtonUp("Jump"))
        {
            if (rigid.velocity.y > 0)
            {
                rigid.velocity = new Vector2(rigid.velocity.x, rigid.velocity.y * fCutJumpHeight);
            }
        }

        if ((fJumpPressedRemember > 0) && (fGroundedRemember > 0))
        {
            fJumpPressedRemember = 0;
            fGroundedRemember = 0;
            rigid.velocity = new Vector2(rigid.velocity.x, fJumpVelocity);
        }

        float fHorizontalVelocity = rigid.velocity.x;
        fHorizontalVelocity += Input.GetAxisRaw("Horizontal");

        if (Mathf.Abs(Input.GetAxisRaw("Horizontal")) < 0.01f)
            fHorizontalVelocity *= Mathf.Pow(1f - fHorizontalDampingWhenStopping, Time.deltaTime * 10f);
        else if (Mathf.Sign(Input.GetAxisRaw("Horizontal")) != Mathf.Sign(fHorizontalVelocity))
            fHorizontalVelocity *= Mathf.Pow(1f - fHorizontalDampingWhenTurning, Time.deltaTime * 10f);
        else
            fHorizontalVelocity *= Mathf.Pow(1f - fHorizontalDampingBasic, Time.deltaTime * 10f);

        rigid.velocity = new Vector2(fHorizontalVelocity, rigid.velocity.y);
    }
}

This is the PlayerInputActions window.

The action type of Move is set to Pass Through and control type is Vector2

\$\endgroup\$
  • \$\begingroup\$ The original code from the tutorial is working as it should and your question is, why is your own code not doing the same? \$\endgroup\$ – Zibelas Nov 16 at 9:15
  • 1
    \$\begingroup\$ You might get a quicker answer if you removed all the parts of your code which are unrelated to jumping. The shorter your code, the more likely people will actually want to read it all. \$\endgroup\$ – Philipp Nov 16 at 11:42
  • \$\begingroup\$ I also fixed the weird and inconsistent indentation in your code. This might also hopefully get you a few more eyeballs. \$\endgroup\$ – Philipp Nov 16 at 11:52
-1
\$\begingroup\$

First thing make sure that the original script is working. In a simple test scene even the original script has bugs and the grounded check is not working correctly when your player gets scaled.

Vector2 v2GroundedBoxCheckScale = (Vector2)transform.localScale + new Vector2(-0.02f, 0);

If my player is for example has a sprite that is 0.25 units big and I scale it to 4x via the transform to make it 1, the original script assumes now that the player is grounded even if he is 4 units away from the ground. And he can do as many jumps in the air as he wants until he is really high.

You don't need to calculate the v2GroundedBoxCheckScale every update, you can do it once in Start and save the value since the scale of your player should not change anymore. To get the actual size, the easiest would be to grab it from the bounds of your renderer.

A simplified version for buffering the jump:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour, PlayerInputActions.IPlayerActions
{
   
   [SerializeField] LayerMask groundLayers;
   [SerializeField] float jumpPressedRememberTime = 0.2f;
   [SerializeField] float groundedRememberTime = 0.25f;  
   [SerializeField] [Range(0, 10)] float jumpHeight = 5f;

   private Rigidbody2D _rb;
   private PlayerInputActions _controls; 
   private Vector2 _playerSize;
   private float _jumpPressedRemember = 0;
   private float _groundedRemember = 0;
   
   private void Awake() 
   {
       _controls = new PlayerInputActions();    
       _controls.Player.SetCallbacks(this);
       _controls.Enable();
   }

   void Start()
   {
      _rb = GetComponent<Rigidbody2D>();    
      _playerSize = (Vector2) GetComponent<SpriteRenderer>().bounds.size + new Vector2(-0.02f, 0);
   }
   
   void PlayerInputActions.IPlayerActions.OnJump(InputAction.CallbackContext context){
       switch (context.phase) {
           case InputActionPhase.Performed:
               _jumpPressedRemember = jumpPressedRememberTime;
               break;
       }
   }

   void Update(){ 
       Vector2 groundedBoxCheckPosition = (Vector2)transform.position + new Vector2(0, -0.01f);
       bool grounded = Physics2D.OverlapBox(groundedBoxCheckPosition, _playerSize, 0, groundLayers);
       _groundedRemember -= Time.deltaTime;
       if (grounded)
       {
           _groundedRemember = groundedRememberTime;
       }
       _jumpPressedRemember -= Time.deltaTime;
       if ((_jumpPressedRemember > 0) && (_groundedRemember > 0))
       {
           _rb.velocity = Vector3.zero;
           _rb.AddForce(Vector2.up * jumpHeight, ForceMode2D.Impulse);
           _groundedRemember = 0;
           _jumpPressedRemember = 0;
       }
   }   
}

I changed the JumpHeight to around 5 to see the actual jump.

Example project can be downloaded from https://github.com/Zibelas/DelayJump - the scene DemoJumo.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ The recent edit still does not answer the question which was actually asked. But I would recommend to determine the size of the player not based on the size of its sprite but on the size and offset of its Collider. \$\endgroup\$ – Philipp Nov 16 at 12:39
  • \$\begingroup\$ @Zibelas I tried this one and added what you wrote but still I have that problem. Did I wrote something wrong or this is not working? \$\endgroup\$ – Ali Nov 17 at 15:34
  • \$\begingroup\$ @Zibelas First I have to solve the sprite renderer problem in order to solve the jumping. The player doesn't have feet, it's just made of an oval and two eyes and I can't change it to one single sprite because I want to animate its eyes. Also I have a question. Do I need to keep everything that you didn't include in your code like some of the variables or OnEnable and OnDisable? \$\endgroup\$ – Ali Nov 21 at 2:58
  • \$\begingroup\$ The posted code is a minimum working example. You do not need to include more to make it work but it is only for jumping, your walking part is missing. You should make an empty scene with just a cube and test it. From there you can switch afterwards to your own character. You can exchange the sprite renderer for the box collider, it has a bounds method as well. \$\endgroup\$ – Zibelas Nov 21 at 6:40
  • \$\begingroup\$ @Zibelas I'm not sure if I understood your question. If you mean groundLayers, I assigned the ground layer that I made to that slot in the inspector. Also every ground object in the scene is on the ground layer. \$\endgroup\$ – Ali Nov 22 at 5:42
0
\$\begingroup\$

When a player GameObject does not jump when you expect it too, sometimes the issue is related to having both a Rigidbody component with gravity enabled while also somehow constraining it's y-value to keep it from falling through the ground plane.

The constraint keeps the object from falling, yet its Rigidbody's velocity is being continually accelerated by gravity every frame.

To determine if this is the case, try resetting the GameObject's velocity before jumping:

This will reset a GameObject's velocity and then jump:

rb.velocity = Vector3.zero;
rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Force);

where rb is a reference to the object's RigidBody component.

| improve this answer | |
\$\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.