Search code examples
c#unity-game-engineinput2d-games

I have some serious problems with the new Input System


I'm making a 2D platformer with Unity. It's been 3 weeks since I decided to use the new Input System (with the Player Input component) to add gamepad and keyboard support but I'm still struggling with it.

To add those features, I decided to use a Player Controller code from GitHub as a guide; but the problem is that it's using the old Input System. So, I need to change the code a little bit.

But I don't know how to write the equivalent of (Input.GetAxisRaw("Horizontal")) in Update().

I've been trying to solve this for a long time, and I would appreciate it if you could guide me.

This is my Player Controller code:

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

public class PlayerController : MonoBehaviour
{
    private PlayerInputActions controls;

    private Rigidbody2D rb;
    private Animator anim;
    private bool facingRight = true;
    private float moveInput;
    public Transform feetPos;
    public float jumpInput;
    public float speed;

    [SerializeField] float JumpVelocity = 5;
    float JumpPressedRemember = 0;
    [SerializeField] float JumpPressedRememberTime = 0.2f;

    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, 1)] float JumpHeight = 0.5f;

    private void Awake()
    {
        controls = new PlayerInputActions();
    }

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
    }

    public void OnMove(InputAction.CallbackContext context)
    {
        moveInput = context.ReadValue<float>();
    }

    public void OnJump(InputAction.CallbackContext context)
    {
        JumpVelocity = context.ReadValue<float>();
    }

    void FixedUpdate()
    {
        rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
      
        if (facingRight == false && moveInput > 0)
        {
            Flip();
        }
        else if (facingRight == true && moveInput < 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);
        Vector2 GroundedBoxCheckScale = (Vector2)transform.localScale + new Vector2(-0.02f, 0);
        bool Grounded = Physics2D.OverlapBox(GroundedBoxCheckPosition, transform.localScale, 0);

        GroundedRemember -= Time.deltaTime;

        if (Grounded)
        {
            GroundedRemember = GroundedRememberTime;
        }

        JumpPressedRemember -= Time.deltaTime;

        if (controls.Player.Jump.triggered)
        {
            JumpPressedRemember = JumpPressedRememberTime;
        }

        if (controls.Player.Jump.triggered)
        {
            if (rb.velocity.y > 0)
            {
                rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * JumpHeight);
            }
        }

        if ((JumpPressedRemember > 0) && (GroundedRemember > 0))
        {
            JumpPressedRemember = 0;
            GroundedRemember = 0;
            rb.velocity = new Vector2(rb.velocity.x, JumpVelocity);
        }

        float HorizontalVelocity = rb.velocity.x;
        HorizontalVelocity += Input.GetAxisRaw("Horizontal");

        if (Mathf.Abs(Input.GetAxisRaw("Horizontal")) < 0.01f)
            HorizontalVelocity *= Mathf.Pow(1f - HorizontalDampingWhenStopping, Time.deltaTime * 10f);
        else if (Mathf.Sign(Input.GetAxisRaw("Horizontal")) != 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);
    }
}

And this is the code that I downloaded from GitHub.

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);
}
}

Solution

  • I wanted to try this out because I hadn't yet implemented jumping in my own game, and I wanted to see how simple it could be with the New Input System. I rather enjoyed this, and one of the coolest parts about event-based input is that you don't have to put everything into a single method!

    Please note that I did this in 3D-- I don't have a 2D game set up yet. I assume you may need to adjust the axis on which force is applied, as well as use the RigidBody2D and 2D colliders.

    For setup, you need a collider on your terrain, and a collider and RigidBody on your character. (I assume you have this set up, but this is for others who find the answer.) The collider on the terrain is needed so the player doesn't fall through the terrain. The collider on the player is for the same reason. The RigidBody allows for physics and calculations around gravity and mass, etc. (You must have gravity enabled for this example!)

    So this example will allow us to apply a force to the player in an upward direction, and then gravity will bring the character back to the terrain.

    First, note that when you create controls and have a C# class auto-generated, you need to have your class inherit that. Your controls class is called PlayerInputActions. I don't see the "Action Map" name, so I will use mine. In the GUI, mine is called "AvatarDefault". The action is called "Jump". If you inspect the auto-generated C# class, you will find the namespace, interface, and methods. My action map becomes an interface when generated, and is named IAvatarDefaultActions. Since you don't list your ActionMap name, I will use mine. Replace it with yours in your code.

    Import your namespace if necessary, and inherit the interface on your player input class.

    Some code removed for brevity!

    public class PlayerController : MonoBehaviour, PlayerInputActions.IAvatarDefaultActions {
        
        // allow changing the force applied when jumping in the editor.
        // note that for a mass of 1 on my character, I had to use a value of about 300
        [SerializeField]
        private float jumpForce;
    
        // track our instance of controls
        private PlayerInputActions controls;
    
        // in awake, we need to create a new instance of controls
        // and bind the action events.
    
        private void Awake()
        {
            controls = new PlayerInputActions();
            // the auto-generated class takes care of all the event registration we need to do!
            // without this, we won't get event notifications.
            controls.AvatarDefault.SetCallbacks(this);
        }
    
        // I chose to explicitly implement the interface for clarity (since I'm still newish)
        void PlayerInputActions.IAvatarDefaultActions.OnJump(InputAction.CallbackContext context)
        {
            // there are several phases, but the only one we should care about is whether
            // the action was performed or not. (You can use an if statement, if you want)
            switch (context.phase)
            {
                case InputActionPhase.Performed:
                    // to separate event handlers from actual code, I've been putting the
                    // actual logic in a separate method
                    this.Jump();
                    break;
            }
        }
    
        public void Jump()
        {
            // you can play around with the ForceMode here, and probably need
            // to use a Vector2 since you're working with 2D.
            // in this example, though, it's applying the jump force on the y axis.
            // a positive value will make the character thrust upward.
            rb.AddForce(transform.up * this.jumpForce, ForceMode.Force);
        }
    
        // You should also have these two methods
        public void OnEnable()
        {
            controls.Enable();
        }
    
        public void OnDisable()
        {
            controls.Disable();
        }
    }
    

    Let me know if this helps. If not, I can attempt to set up a 2D environment. It looks like the only thing that would change for 2D would be to use ForceMode2D.Force.

    For reference, this is what my input setup looks like. I have "Jump" set to a "Button" action type, because I only care if it was pressed or not. enter image description here

    Movement fixes update

    As for the issue with movement, the input system is meant to easily pass a Vector2 for stick and D-pad movement. so the value coming through would have movement.x and movement.y to get the horizontal and vertical input, respectively.

    This is how I have my movement action set up: enter image description here

    And this is how I have the Up control mapped to the W key. (The others are similar, but mapped to the appropriate keys.) enter image description here

    Here's my code to set the movement variable.

    
        private Vector2 moveInput;
    
        void PlayerInputActions.IAvatarDefaultActions.OnMove(InputAction.CallbackContext context)
        {
            moveInput= context.ReadValue<Vector2>();
        }
    
    

    And now you just use moveInput.x in your update code for the horizontal value!