Search Unity

Question New Input System, hold WASD to move character (2D)

Discussion in 'Input System' started by PaulKhill, Sep 11, 2020.

  1. PaulKhill

    PaulKhill

    Joined:
    Jul 30, 2020
    Posts:
    15
    I've been banging my head on my keyboard for 2 full days and I just can't wrap my head around how the new system works. I must be missing something, I just don't get the general gist of everything.The new system seems quite solide and very versatile, but most of the video I find are outdated and I really feel the documentation is missing key steps for me to climb on that train.

    I'm trying to move from the good ol' GetAxis["Horizontal"] to the new input system to get my character to move on an 2D-side scroller. And I can't find how to make it work.

    Right now, I've tried using the OnMove() call using the Broadcast behavior. It works perfectly for jumping, but when it comes to moving around, I have to bash my keys like a maniac to have my character move 1 pixel. And holding the key down doesn't work either. I've tried switching to Hold, but understand that Hold will trigger IF the key is held, not "as long as" the key is held.

    It seems like I'm supposed to call something within Update or FixedUpdate. Which makes sense. But I can't wrap my head around PlayerInput, InputActions, InputActionMap, nor where or when to call them... Some threads say you must Enable and Disable them, but I don't even get how you're supposed to instanciate them at first... I just don't get it.

    Can anyone take the time to walk me through the general gist of the whole system ? Or just point me toward the 2 or 3 key elements I'm missing to finally get it working ?

    Thanks a lot !
     
  2. PaulKhill

    PaulKhill

    Joined:
    Jul 30, 2020
    Posts:
    15
    Alright, I'm gonna solve that myself, as I stumbled upon the Demo Project which had a sort of similar approach.

    Here's what I ended up with :

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.InputSystem;
    4.  
    5. public class MouseCharacterController : MonoBehaviour
    6. {
    7.    
    8.  
    9.     public float speed = 5f;
    10.  
    11.     [Header("Input")]
    12.     public PlayerInput playerInput;
    13.     private Vector3 inputDirection;
    14.     private bool currentInput = false;
    15.  
    16.     void Update()
    17.     {
    18.         CalculateMovementInput();
    19.     }
    20.     void FixedUpdate()
    21.     {
    22.         MoveThePlayer();
    23.     }
    24.  
    25.     void CalculateMovementInput()
    26.     {
    27.         if (inputDirection == Vector3.zero)
    28.         {
    29.             currentInput = false;
    30.         }
    31.         else if (inputDirection != Vector3.zero)
    32.         {
    33.             currentInput = true;
    34.         }
    35.     }
    36.  
    37.     void MoveThePlayer()
    38.     {
    39.         if (currentInput == true)
    40.         {
    41.             Vector3 movement = inputDirection.normalized * speed * Time.deltaTime;
    42.             transform.position += (movement * Time.deltaTime * speed);
    43.         }
    44.  
    45.     }
    46.  
    47.     public void OnMove(InputValue value)
    48.     {
    49.         inputDirection = value.Get<Vector2>();
    50.     }
    51.  
    52. }
    53.  
    Pretty much ditched all I didn't need for a 2D platformer. It works for me. Actually didn't need to invoque or enable anything...

    I'm still unsure of those enable parts, but it's not blocking me anymore. If anyone has any good places where I can get a better understanding of these parts, happy to read ! For everyone else, hope this comes useful.
     
    ChaosRobin likes this.
  3. djweaver

    djweaver

    Joined:
    Jul 4, 2020
    Posts:
    105
    AFAIK, the InputAction.Enable() and .Disable() are for when you have instantiated the input actions programmatically. When you are using a Player Input component however, you do not need to do these steps.

    As for the general gist:

    You can use the new input system in a variety of different ways.

    1. You can hard code each action without ever having the need to create the input action asset or player input components. This is a 100% code implementation.

    2. You can create the input action asset, and reference it to essentially create the events/callbacks that would have otherwise been generated by the auto-generate class option

    3. You can create the input action asset and have it create the class for you, and reference the class (or the interface it generates at the bottom of the script) to use the events it has created for you

    4. (Easiest) You can create the input action asset AND the player input component and use the component to broadcast the events directly to your script

    In any case, you are generally dealing with events and their callback contexts. This "context" is a struct passed into the event handling callback that contains data about the input action that has been executed, including the key being pressed, the phase (whether it has just been initiated, has been performed, or has been stopped or canceled). In my current prototype, I use method 4 from the above list to activate a shield when the player holds the right mouse button down:

    Code (CSharp):
    1.     public class Shield : MonoBehaviour
    2.     {
    3.         private void Start()
    4.         {
    5.             gameObject.SetActive(false);
    6.         }
    7.         public void OnShield(InputAction.CallbackContext context)
    8.         {
    9.  
    10.             if (context.started)
    11.             {
    12.                 State.isShielded = true;
    13.                 gameObject.SetActive(true);
    14.             }
    15.             else if (context.canceled)
    16.             {
    17.                 State.isShielded = false;
    18.                 gameObject.SetActive(false);
    19.             }
    20.         }
    21.     }

    Thats how I have implemented it at least. The problem (I have found) with delving deeper into this is that if you are new like me, its hard to understand where the new input system falls into the typical game design patterns you hear about so often on youtube and in other channels. You can find a tutorial on how to implement the observer pattern... and you can find a video on how to implement the new input system. But you'll never find a video on both, or how these systems are supposed to interact with each other, or if its even necessary or if its redundant. This perplexes the newbie like me. Again, you can find a video on events... delegates... unity events even. But you'll never find a video on one of those systems being used in concurrency with the new input system, so its hard to tell if you need one or the other or both and why or why not. Like since I'm using the new input system, do I want to create separate event system for UI? Do I need a state machine if I have events being raised by the Player Input component that can just as easily access a static property? Am I abiding by the principle of separation of concerns by handling the input callback in the same script as the functions it performs? Should I group all my input callbacks into a general input controller script? If so, why would unity events be a pillar of this system, allowing you to route each callback to multiple scripts across different objects? So many questions, and nary an answer that I have found. But I digress, if you are looking at purely the input system itself, it seems highly configurable, modular and according to people much more knowledgeable than I, scalable.
     
  4. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,290
    To that last comment: If you are using scriptable events (like mine: https://gitlab.com/lemuria.org/observables) you simply use "Invoke Unity Event" for the behaviour and then drag the scriptable object into the slot, selecting "NotifyListener" (or whatever it's called on other people's implementations). Then all you need to do is have whoever wants to know of an input event listen and you're done. It's very straightforward and you don't need to plaster Player Input components all over your project.