Search Unity

[Solution] Migrating Platformer input script from Old to New Input System

Discussion in 'Input System' started by ben4d85, Jan 12, 2020.

  1. ben4d85

    ben4d85

    Joined:
    Dec 26, 2018
    Posts:
    47
    Below please find my custom input manager script using the Old Input System. This is based on the script from Create A 2D Platformer - Unite Berlin Training Day. It works like a charm, no problems whatsoever.

    I have spend this evening exploring the docs for the New Input System, watching various YouTube videos, and reading through this forum. I found it challenging to migrate my script from the Old to the New Input System, hence started this thread.

    Eventually, I managed to wrap my head around it. I am sharing both my original script using the Old Input System and my new, migrated script using the New Input System in the hopes that it may help others make sense of things.

    I would really appreciate any feedback on my solution (see my reply below).

    Code (CSharp):
    1. // Ensure this script runs before all other player scripts to prevent laggy inputs.
    2. [DefaultExecutionOrder(-100)]
    3.  
    4. // Custom input class using the Old Input System
    5. public class PlayerInputCustomOld : MonoBehaviour
    6. {
    7.     // Horizontal and vertical axis input
    8.     [HideInInspector] public float horizontal;
    9.     [HideInInspector] public float vertical;
    10.     [HideInInspector] public float horizontalRaw;
    11.     [HideInInspector] public float verticalRaw;
    12.  
    13.     // Jump button
    14.     [HideInInspector] public bool jumpPressed;
    15.     [HideInInspector] public bool jumpHeld;
    16.     [HideInInspector] public bool jumpReleased;
    17.  
    18.     // Bool used to keep input in sync between Update() and FixedUpdate()
    19.     private bool readyToClear;
    20.  
    21.     // Update is called once per frame
    22.     void Update()
    23.     {
    24.         // Clear out existing input values
    25.         ClearInput();
    26.  
    27.         // If the Game Manager says the game is over, exit
    28.         if (GameManager.Instance.IsGameOver() || GameManager.Instance.IsFreezeInput()) return;
    29.  
    30.         // Process inputs from keyboard, mouse, gamepad, etc.
    31.         ProcessInput();
    32.  
    33.         // Clamp the horizontal axis input to be between -1 and 1
    34.         horizontal = Mathf.Clamp(horizontal, -1f, 1f);
    35.  
    36.         // Clamp the vertical axis input to be between -1 and 1
    37.         vertical = Mathf.Clamp(vertical, -1f, 1f);
    38.     }
    39.  
    40.     void FixedUpdate()
    41.     {
    42.         // Set a flag that lets inputs to be cleared out during the next Update().
    43.         // This ensures that all code gets to use the current inputs.
    44.         readyToClear = true;
    45.     }
    46.  
    47.     private void ClearInput()
    48.     {
    49.         // If we're not ready to clear input, exit
    50.         if (!readyToClear) return;
    51.  
    52.         // Reset all axis
    53.         horizontal = 0f;
    54.         vertical = 0f;
    55.         horizontalRaw = 0f;
    56.         verticalRaw = 0f;
    57.  
    58.         // Reset all buttons
    59.         jumpPressed = false;
    60.         jumpHeld = false;
    61.         jumpReleased= false;
    62.  
    63.         // Just cleared the input
    64.         readyToClear = false;
    65.     }
    66.  
    67.     private void ProcessInput()
    68.     {
    69.         // Accumulate axis input
    70.         horizontal += Input.GetAxis("Horizontal");
    71.         vertical += Input.GetAxis("Vertical");
    72.         horizontalRaw += Input.GetAxisRaw("Horizontal");
    73.         verticalRaw += Input.GetAxisRaw("Vertical");
    74.  
    75.         // Handle button inputs
    76.         jumpPressed = jumpPressed || Input.GetButtonDown("Jump");
    77.         jumpHeld = jumpHeld || Input.GetButton("Jump");
    78.         jumpReleased = jumpReleased || Input.GetButtonUp("Jump");
    79.     }
    80. }
    81.  
     
    Last edited: Jan 12, 2020
  2. ben4d85

    ben4d85

    Joined:
    Dec 26, 2018
    Posts:
    47
    Below please find my new, migrated script using the New Input System.

    So far, this appears to work fine for my 2D platformer. However, I would appreciate feedback, specifically:
    • Is this the intended way? Is there anything I could do smarter?
    • Is my DefaultExecutionOrder correct?
    • I am not entirely sure if my implementation of axis raw is correct?
    For horizontal and vertical movement, I am using separate x and y axis. For buttons, I have provided two different button implementations. Jump only detects if the button was pressed. Grab detects if the button was pressed, held or released.

    For me, the key to wrapping my head around this was to understand that an InputAction can be cast to a ButtonControl, and then:
    • GetButtonDown becomes wasPressedThisFrame
    • GetButton becomes isPressed
    • GetButtonUp becomes wasReleasedThisFrame
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.InputSystem;
    5. using UnityEngine.InputSystem.Controls;
    6.  
    7. // Ensure this script runs before all other player scripts to prevent laggy inputs.
    8. [DefaultExecutionOrder(-90)]
    9.  
    10. // Custom input class using the New Input System
    11. public class PlayerInputCustomNew : MonoBehaviour
    12. {
    13.     //=====================================================
    14.     // Public properties
    15.     // (to be accessed by external scripts)
    16.     //=====================================================    
    17.  
    18.     // Horizontal and vertical axis input
    19.     [HideInInspector] public float horizontal;
    20.     [HideInInspector] public float vertical;
    21.     [HideInInspector] public float horizontalRaw;
    22.     [HideInInspector] public float verticalRaw;
    23.  
    24.     // Jump button
    25.     [HideInInspector] public bool jumpPressed;
    26.  
    27.     // Grab button
    28.     [HideInInspector] public bool grabPressed;
    29.     [HideInInspector] public bool grabHeld;
    30.     [HideInInspector] public bool grabReleased;
    31.  
    32.     //=====================================================
    33.     // State
    34.     //=====================================================
    35.  
    36.     // Bool used to keep input in sync between Update() and FixedUpdate()
    37.     private bool readyToClear;
    38.  
    39.     // Input actions wrapper (generated by Unity Input System Asset)
    40.     private PlayerInputActions controls;
    41.  
    42.     //=====================================================
    43.     // Methods
    44.     //=====================================================
    45.  
    46.     //
    47.     private void Awake()
    48.     {
    49.         // Instantiate input actions
    50.         controls = new PlayerInputActions();
    51.     }
    52.  
    53.     // When this component is enabled
    54.     private void OnEnable()
    55.     {
    56.         // Enable input actions
    57.         controls.Player.Enable();
    58.     }
    59.  
    60.     // When this component is disabled
    61.     private void OnDisable()
    62.     {
    63.         // Disable input actions
    64.         controls.Player.Disable();
    65.     }
    66.  
    67.     // Update is called once per frame
    68.     void Update()
    69.     {
    70.         // Clear out existing input values
    71.         ClearInput();
    72.  
    73.         // If the Game Manager says the game is over, exit
    74.         if (GameManager.Instance.IsGameOver() || GameManager.Instance.IsFreezeInput()) return;
    75.  
    76.         // Process player input using the new input system
    77.         ProcessPlayerInputNew();
    78.     }
    79.  
    80.     // Fixed update is called once per physics step
    81.     void FixedUpdate()
    82.     {
    83.         // Set a flag that lets inputs to be cleared out during the next Update().
    84.         // This ensures that all code gets to use the current inputs.
    85.         readyToClear = true;
    86.     }
    87.  
    88.     // Clear input, if we are ready
    89.     private void ClearInput()
    90.     {
    91.         // If we are not ready to clear input, exit
    92.         if (!readyToClear) return;
    93.  
    94.         // Reset all axis
    95.         horizontal = 0f;
    96.         vertical = 0f;
    97.         horizontalRaw = 0f;
    98.         verticalRaw = 0f;
    99.  
    100.         // Reset jump button
    101.         jumpPressed = false;
    102.  
    103.         // Reset grab button
    104.         grabPressed = false;
    105.         grabHeld = false;
    106.         grabReleased = false;
    107.  
    108.         // Just cleared the input
    109.         readyToClear = false;
    110.     }
    111.  
    112.     // Process player input using the new input system
    113.     private void ProcessPlayerInputNew()
    114.     {
    115.         //=================================================
    116.         // Horizontal and vertical axis
    117.         //=================================================
    118.  
    119.         // Accumulate axis input
    120.         // These values can be e.g. -2, -1, 0, 1, 2
    121.         horizontal += controls.Player.HorizontalAxis.ReadValue<float>();
    122.         vertical += controls.Player.VerticalAxis.ReadValue<float>();
    123.  
    124.         // Accumulate raw axis input
    125.         horizontalRaw += horizontal;
    126.         verticalRaw += vertical;
    127.  
    128.         // Clamp axis input to be between -1 and 1
    129.         // These values are now limited to -1, 0, 1
    130.         horizontal = Mathf.Clamp(horizontal, -1f, 1f);
    131.         vertical = Mathf.Clamp(vertical, -1f, 1f);
    132.  
    133.         //=================================================
    134.         // Jump button
    135.         //=================================================
    136.  
    137.         // If the jump button was pressed this frame,
    138.         // or was pressed in a previous frame, but was not yet cleared
    139.         jumpPressed = jumpPressed || controls.Player.Jump.triggered;
    140.  
    141.         //=================================================
    142.         // Grab button
    143.         //=================================================
    144.  
    145.         // Cast input action to button control to get access to desired functionality
    146.         var grabButtonControl = (ButtonControl)controls.Player.Grab.controls[0];
    147.  
    148.         // Check if the grab button was pressed this frame (comparable to GetButtonDown)
    149.         bool grabPressedThisFrame = (grabButtonControl != null && grabButtonControl.wasPressedThisFrame);
    150.         // Or was pressed in a previous frame, but was not yet cleared
    151.         grabPressed = grabPressed || grabPressedThisFrame;
    152.  
    153.         // Check if the grab button was held this frame (comparable to GetButton)
    154.         bool grabHeldThisFrame = (grabButtonControl != null && grabButtonControl.isPressed);
    155.         // Or was pressed in a previous frame, but was not yet cleared
    156.         grabHeld = grabHeld || grabHeldThisFrame;
    157.  
    158.         // Check if the grab button was released this frame (comparable to GetButtonUp)
    159.         bool grabReleasedThisFrame = (grabButtonControl != null && grabButtonControl.wasReleasedThisFrame);
    160.         // Or was pressed in a previous frame, but was not yet cleared
    161.         grabReleased = grabReleased || grabReleasedThisFrame;
    162.     }
    163. }
    My Input Actions (configured via the Unity Editor's Input Actions tab)
    1. HorizontalAxis:
      • Action: Action Type: Value, Control Type: Axis
      • Binding: Stick/X (specific to my gamepad)
    2. VerticalAxis
      • Action: Action Type: Value, Control Type: Axis
      • Binding: Stick/Y (specific to my gamepad)
    3. Jump
      • Action: Action Type: Button
      • Binding: Button 3 (specific to my gamepad)
      • Interactions: None (this is important, adding Press, Hold, etc interactions may cause unexpected behaviour)
    4. Grab
      • Same as Jump, just a different button
     
    Last edited: Jan 12, 2020
    TheSleepyKoala likes this.
  3. FileThirteen

    FileThirteen

    Joined:
    Oct 23, 2012
    Posts:
    40
    Have you tested this with different controllers? M+K, different controllers, at the same time? I get the feeling that
    var grabButtonControl = (ButtonControl)controls.Player.Grab.controls[0];
    isn't doing exactly what you want it to do. You are grabbing a control from the many controls of the action Grab. Grab could be mapped with a mouse input, gamepad input, and any other inputs. In this case you seem to only be grabbing the first one. I could be wrong because I'm still learning the system as well.
     
    ben4d85 likes this.
  4. ben4d85

    ben4d85

    Joined:
    Dec 26, 2018
    Posts:
    47
    Thank you for your feedback. Of course my approach is only a starting point. A lot more could be done regarding multiple control schemes, e.g. mouse and keyboard vs gamepad, connecting and disconnecting input devices, etc.

    However, in my case, the only control I have set up is gamepad and, as far as I understand it, that corresponds to
    .controls[0]
    . So while it could be mapped to many controls in theory, in practice it is not, because I have not set it up to be.

    That being said, there also is
    .activeControl
    , but I had issues using that in combination with
    .wasReleasedThisFrame
    , so decided to opt for
    .controls[0]
    instead, which for the time being appears to work fine for me.
     
    Last edited: Feb 2, 2020
  5. FileThirteen

    FileThirteen

    Joined:
    Oct 23, 2012
    Posts:
    40
    Yup that makes sense. The new system seems pretty versatile but I don't know if I like it yet to be honest.
     
  6. FantacticHD

    FantacticHD

    Joined:
    May 28, 2019
    Posts:
    1
    ok, how did you use .wasreleasedthisframe , is that a custom function? sorry i'm a bit slow at this
     
  7. ben4d85

    ben4d85

    Joined:
    Dec 26, 2018
    Posts:
    47
    No,
    wasReleasedThisFrame
    (just like
    isPressed
    and
    wasPressedThisFrame
    ) is part of
    ButtonControl
    .
     
  8. akMakesGames

    akMakesGames

    Joined:
    Nov 20, 2019
    Posts:
    1
    Thank you for this post, helped me a lot as I am adapting my 2D platformer for the new Input System.

    I see what you are saying about:
    (ButtonControl)controls.Player.[MY_ACTION].activeControl

    I notice a different behavior between using that code above and similar to what you have:
    (ButtonControl)controls.Player.[MY_ACTION].controls[0]


    In my case, I am handling a gamepad and a keyboard as input devices. When using
    controls[0] 
    (gamepad) or
    controls[1] 
    (keyboard) I am able to handle button up/down actions with
    triggered/wasReleasedThisFrame
    respectively.
    But if I use
    .activeControl
    I am not able to detect
    .wasReleasedThisFrame
    in the same way. Any ideas on a solution for this? Perhaps I should just be detecting the active control and then finding the index of that control in my .controls[] array.
     
    ben4d85 likes this.
  9. ben4d85

    ben4d85

    Joined:
    Dec 26, 2018
    Posts:
    47
    I had the exact same problem with
    .activeControl
    and
    .wasReleasedThisFrame
    . Hence why I decided to use
    .controlls[0]
    instead for the meantime. No solution, yet I am afraid.

    Just thinking out loud here, so I might well be wrong, but could it be that
    .activeControl
    only works when a button is pressed or held, meaning, when a button is released, the control is not "active" anymore because nothing is pressed or held?
     
    Last edited: Feb 2, 2020
    akMakesGames likes this.
  10. AaronRawlinson

    AaronRawlinson

    Joined:
    Jun 10, 2018
    Posts:
    7
    Hey Guys,

    I'm currently working on a couch co-op game (student project) and I've integrated the New Input System with Unity's Standard Assets Third Person Controller. I've set it up so a player is added when a certain button is pressed (Button South for GamePad and Space for Keyboard) and this works fine in terms of movement. It adds 4 players after four presses as intended (Although, I should point out that it shouldn't be adding more than two as I'm testing with an Xbox Controller and Keyboard, so that's only two Input devices). However, they are all controlled by the same controller and move together and can't be controlled individually.

    I can share files on request.

    Any advice?