Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Reading new Input System inputs in C#

Discussion in 'Input System' started by Daedolon, Jun 28, 2021.

  1. Daedolon

    Daedolon

    Joined:
    Dec 26, 2014
    Posts:
    3
    I'm trying to understand the easiest way to read inputs from the new Input System in code in a single class.

    I want to read all the player input in a single class (called from within Update()) and then process it later down the line in Update() and FixedUpdate() respectively).

    For instance, I would have something like this (just a simple pseudocode example) with the old input system:

    Code (CSharp):
    1.     private Vector2 playerMovement = Vector2.zero;
    2.     private bool playerAttacking = false;
    3.  
    4.     void GetPlayerInput()
    5.     {
    6.         playerMovement.x = Input.GetAxisRaw("Horizontal");
    7.         playerMovement.y = Input.GetAxisRaw("Vertical");
    8.  
    9.         if (Input.GetKey("Attack"))
    10.             playerAttacking = true;
    11.         else
    12.             playerAttacking = false;
    13.     }
    How do I implement something like this with the new Input System and which Behaviour (Send Messages, Broadcast Messages, Invoke Unity Events or Invoke C Sharp Events) would I need for it?

    The two former ones all rely on multiple classes which is messy and not what I'm looking for (a single class to handle reading all input with a secondary class processing the player movement).

    Then the Invoke Unity Events behaviour, although apparently suggested, relies almost entirely on Unity Editor side references which are very detached from the code side of the project, break easily (references) and just aren't accessible through code properly.

    That leaves me (as far as I understand the new Input System currently) only with the option to go for Invoke C Sharp Events. The Generate C# Class function generates a C# source file that seems to overtly complicate things by taking everything into account and none of the code or any examples I've been able to find online have suggested a way of doing things in a fashion similar to my code example above.

    I might not be understanding the new Input System correctly, which is entirely possible as I'm just a hobbyist programmer, but I'm sure there must be some sort of a reasonably simple and robust way of just reading the status of the inputs based on their Action names.

    Any help would be immensely appreciated, as the new Input System is still relatively new, especially in terms of available tutorials and documentation of clarity, thanks!
     
    erfanZed likes this.
  2. andrew_oc

    andrew_oc

    Unity Technologies

    Joined:
    Apr 16, 2021
    Posts:
    77
    Hi @Daedolon

    There are a bunch of different ways to do this, which can definitely make it a little harder to get started. Here's a simple, code-only, example to help:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3.  
    4. public class PlayerMovement : MonoBehaviour
    5. {
    6.     private Vector2 m_PlayerMovement;
    7.     private InputAction m_MoveAction;
    8.     private InputAction m_AttackAction;
    9.  
    10.     void Start()
    11.     {
    12.         m_MoveAction = new InputAction("Move");
    13.         m_MoveAction.AddBinding("<Gamepad>/leftStick");
    14.         m_MoveAction.Enable();
    15.  
    16.         m_AttackAction = new InputAction("Attack", binding: "<Gamepad>/buttonSouth");
    17.         m_AttackAction.Enable();
    18.     }
    19.  
    20.     void Update()
    21.     {
    22.         m_PlayerMovement = m_MoveAction.ReadValue<Vector2>();
    23.         if(m_PlayerMovement != Vector2.zero)
    24.             Debug.Log("Vector = " + m_PlayerMovement);
    25.        
    26.         var attacking = m_AttackAction.ReadValue<float>();
    27.         if(Mathf.Approximately(attacking, 1f))
    28.             Debug.Log("Attacking");
    29.     }
    30. }
    31.  
    You can get gradually more sophisticated from there. For example, you can define your input actions in an Input Action Asset in the editor and add a reference to the asset in your script. Then you can access the different actions using something like:

    Code (CSharp):
    1. m_inputActions.FindAction("Move")
    Input actions have methods like WasPressedThisFrame, WasReleasedThisFrame, and IsPressed to help you get closer to Input.GetKey functionality. If you'd rather get notified in a callback when an input event occurs, you can attach a callback handler to an input action like this:

    Code (CSharp):
    1. m_MoveAction.performed += context =>
    2. {
    3.       m_PlayerMovement = context.ReadValue<Vector2>();
    4. };
    It sounds like you're also using the PlayerInput class here. Note that this is really just a higher level wrapper around the concepts in this post (plus lots of extra goodies for local multiplayer games), so you don't necessarily need it when getting started. That said, if you do want to use it, and you want a code-first approach, this is one way you could hook up event handlers to use Invoke C# Events behaviour:

    Code (CSharp):
    1. GetComponent<PlayerInput>().onActionTriggered += context =>
    2. {
    3.       if(context.action == m_InputAsset.FindAction("Move"))
    4.            m_PlayerMovement = context.ReadValue<Vector2>();
    5. };
    So basically, when the player triggers any of your input actions, the onActionTriggered handler will be called, and you can use the passed CallbackContext to figure out what action fired, and do whatever you need to in there.

    Hope this helps
     
    rizorpolder, erfanZed and Daedolon like this.
  3. Fera_KM

    Fera_KM

    Joined:
    Nov 7, 2013
    Posts:
    307
    Might as well share because I was struggling a lot with this
    (Note the PlayerInput component is added to a GameManager Object which is used as a Singleton, and this script would be on another object)

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3.  
    4.  
    5. public class TestInput : MonoBehaviour
    6. {
    7.     private InputAction testAction;
    8.     private PlayerInput playerInput;
    9.  
    10.     private void Awake()
    11.     {
    12.         //Getting the PlayerInput component
    13.         playerInput = GameManager.instance.GetComponent<PlayerInput>();
    14.  
    15.         testAction = playerInput.actions.FindAction("Jump");
    16.         testAction.performed += DoJumpAction;
    17.     }
    18.  
    19.     private void OnEnable()
    20.     {
    21.         testAction.Enable();
    22.     }
    23.  
    24.     private void OnDisable()
    25.     {
    26.         testAction.Disable();
    27.     }
    28.  
    29.     void DoJumpAction(InputAction.CallbackContext context)
    30.     {
    31.         Debug.Log("JumpAction Success!!");
    32.     }
    33. }
    Might not be the best way, but it works
     
    VictorHHT likes this.