Unity Input System, from Basic Principles
Tags: gamedev, unity3d
Published: 2020-11-02 00:11:22

---
title: Unity Input System, from Basic Principles
published: true
description: We go over the basic principles of input and movement, showing code examples of using Unity's new Input System to move a player.
tags: gamedev, unity3d
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/i2eu918qx0vorbf2n93o.jpg
canonical_url: https://blog.eyas.sh/2020/10/unity-for-engineers-pt3-input-system/
---

In the third installment of the **[Unity for Software Engineers](https://blog.eyas.sh/tag/unity-for-software-engineers)** series, we cover Unity's [Input System](https://docs.unity3d.com/Packages/com.unity.inputsystem@latest/) from basic principles.

---

Handling player input will likely be among the first game systems you implement. Unity Technologies [unveiled a new Input System](https://blogs.unity3d.com/2019/10/14/introducing-the-new-input-system/) in 2019 intended to replace their previous system. While the new Input System allows building on more idiomatic Unity patterns, it can also be more challenging for a newcomer to pick these up. This is especially true because the Input System leans on configurable assets and emphasizes Editor usage. The Input System's [Quick start guide](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/QuickStartGuide.html) is helpful, but only once the Input System and Unity Events' basic concepts are understood.

Much of the newer pieces of the Unity Engine are being released as encapsulated [packages](https://docs.unity3d.com/Manual/PackagesList.html). Unity packages are installed from the _Unity Package Manager_ UI in the editor (open it in _Window > Package Manager_). The package  updates a `manifest.json` file that has a `"dependencies"` entry identical to a [`package.json`](https://docs.npmjs.com/files/package.json#dependencies). The
package manager resolves dependencies and writes to a `package-lock.json`. You can install the Unity Input System from the Package manager, which adds a dependency on `com.unity.inputsystem` in your manifest.

The Input System makes it easy to create player-configurable cross-platform game controls through assets known as Input Action Maps. The system also exposes an Editor- and event-based interface to specify Input behavior.

## Programmatic Use

If you'd like to make progress on a proof of concept quickly, you might initially choose to sidestep Input Action maps and programmatically interrogate the inputs.

Here's a basic example covering the most important concepts:

```cs
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerControlHandler : MonoBehaviour
{
  /// <summary>
  /// The speed of a moving player in m/s.
  /// </summary>
  [SerializeField] private float _moveSpeed = 5f;

  void Update()
  {
    // The last used (or last connected) Gamepad.
    // null if there is no connected gamepad.
    Gamepad gamepad = Gamepad.current;
    // The return value of `.current` cann be null.
    if (gamepad == null)
    {
      return;
    }

    // buttonSouth is a ButtonControl.
    if (gamepad.buttonSouth.wasPressedThisFrame)
    {
      Fire();
    }

    // leftStick is a StickControl, which eventually
    // inherits from InputControl<Vector2>.
    //
    // InputControl<T> exposes a ReadValue() method
    // returning T. Typically, this is a zero-value
    // when the control has not been actuated in a
    // given frame.
    //
    // Here, move is a Vector2 between (-1, -1) and
    // (+1, +1) indicating the most recent direction
    // of the stick (as of this frame).
    Vector2 move = gamepad.leftStick.ReadValue();

    Vector3 moveThisFrame =
      // Make movement speed frame-rate independent
      Time.deltaTime * _moveSpeed *
      (
        (Vector3.right * move.x) +
        (Vector3.forward * move.y)
      );

    transform.position += moveThisFrame;
  }

  void Fire() {} // TODO: Implement.
}
```
or:

```cs
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerControlHandler : MonoBehaviour
{
  /// <summary>
  /// The speed of a moving player in m/s.
  /// </summary>
  [SerializeField] private float _moveSpeed = 5f;

  void Update()
  {
    // Control input with WASD + Mouse
    Keyboard keyboard = Keyboard.current;
    Mouse mouse = Mouse.current;

    if (keyboard == null || mouse == null)
    {
      return;
    }

    // Mouse.leftButton is also a Button control.
    if (mouse.leftButton.wasPressedThisFrame)
    {
      Fire();
    }

    // Every keyboard key is a KeyControl, which
    // inherits ButtonControl.
    //
    // ButtonControl extends InputControl<float>
    // which represents a control with a value 0
    // when not pressed and 1 when pressed.
    //
    // Here we represent our movement as vertical
    // and horizontal axes, each from -1 to +1.
    float forward = keyboard.wKey.ReadValue()
                  - keyboard.sKey.ReadValue();
    float horizontal = keyboard.dKey.ReadValue()
                     - keyboard.aKey.ReadValue();
    Vector3 moveThisFrame =
      // Make movement speed frame-rate independent
      Time.deltaTime * _moveSpeed *
      (
        (Vector3.right * horizontal) +
        (Vector3.forward * forward)
      );

    transform.position += moveThisFrame;
  }

  void Fire() {} // TODO: Implement.
}
```

As you can see, `UnityEngine.InputSystem` exposes various input devices, each with a static `current` member returning the device if connected[^1]. See the documentation for [`UnityEngine.InputSystem.Gamepad`, for example](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Gamepad.html).

Each Input Device exposes a number of Controls. These all implement the **[`InputControl<T>`](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.InputControl-1.html)** class. Significantly, `InputControl<T>` exposes **`T ReadValue()`** which returns the current value. This can be repeatedly called in `Update()` or `FixedUpdate()` to respond to input.

Most buttons (Gamepad buttons, Mouse clicks, Keyboard keys) eventually inherit **[`ButtonControl`](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Controls.ButtonControl.html)**, which also introduces useful properties such as `isPressed`, `wasPressedThisFrame`, and `wasReleasedThisFrame`. A `ButtonControl` eventually inherits from `InputControl<float>`. On digital devices, controls can have a value of either `0` or `1`, but on analog- or pressure-sensitive devices, this can smoothly vary between `0` and `1`.

Other controls, like `DpadControl` and `StickControl` eventually inherit from **`InputControl<Vector2>`**, which gives us the combined player input in 2D space.

You can define a _composite_ `Vector2` control using [`Vector2Composite`](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Composites.Vector2Composite.html) from `UnityEngine.InputSystem.Composites`. Doing so is helpful, for example, to create `WASD` as a single `Vector2` composite. At that point, however, you might as well go for the more expressive approach of using Input Actions and Input Action Map assets, rather than the purely programmatic approach.

An advantage of programmatic handling of input is that you can get started quickly. The moment you want to make controls player-configurable or handle multiple devices, this code becomes unwieldy.

[^1]:
    Local multiplayer games can't count on these static accessors, as the `current` Gamepad is whichever device most recently used.

## Input Actions

Last week, I warned against the [impulse of keeping everything in code](https://blog.eyas.sh/2020/10/unity-for-engineers-pt2-six-practices#keep-it-in-code), when the editor is at your disposal. With a basic understanding of the fundamentals from the code above, I recommend using the higher-level concept of _"Input Actions"_.

<figure>
![An Empty PlayerInput component](https://dev-to-uploads.s3.amazonaws.com/i/ij3f3ztve3sdqme699t0.png)
<figcaption>
A recently-created Player Input component allows its Game Object to respond to inputs from a given Input Action Map asset.
</figcaption>
</figure>

To start making a Game Object controllable, let's add a _Player Input_ component to it. A component takes an **Input Actions
_[asset](https://blog.eyas.sh/2020/10/unity-for-engineers-pt1-basic-concepts#asset)_** and uses it to define behaviors for each **Input Action**.

To start, we'll use the _Create Actions..._ button to create a default _Input Action Asset_. Unity will pedagogically create an asset pre-populated with a cross-platform control scheme, including movement (on PC: WASD), look (on PC: mouse movement), and firing.

<figure>
![A populated Input Action Asset. Shows two "Action Maps" for Player and UI. The Player map is selected, with definitions for Move, Look, Fire, and Jump actions.](https://dev-to-uploads.s3.amazonaws.com/i/334nkpv5ghcibq1zjsy1.png)

<figcaption>
A populated Input Action asset loosely modified from the default asset.
</figcaption>
</figure>


An **Input Action Asset** describes a game's entire control schemes. The asset comprises multiple **Action Maps**, each corresponding to a control scheme active in a given context. By default, Unity creates a "Player" (in-game) and a "UI" (e.g., in menus) map.

Here, you can see that `Move` is a `Vector2` action. The _Gamepad_ control scheme uses a stick control, while the _Keyboard/Mouse_ scheme uses a composite `Vector2`. Keyboard/mouse bindings here allow the player to use either WASD _or_ arrow keys.

Modify your Action Map as needed. For now, don't worry about _Interactions_ or _Processors_; these allow you to constrain or pre-process action values before passing them to consumers.

{%youtube fKutPMKC1BE%}

When a _Player Input_ component has an input action asset, it allows you to set a _Default Scheme_ and a _Default Map_.

A _Player Input_ component has four potential [_behaviors_](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/Components.html#notification-behaviors):

1. **Send Messages**: For each Input Action that actuates, calls    [`SendMessage`](https://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html) on the Game Object, with the action name (e.g., `OnFire`, `OnLook`, `OnMove`) and an [`InputValue`](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.InputValue.html).

    An `InputValue` exposes boolean property `isPressed`, and the generic `TValue Get<TValue>()` method. You will need to make sure the generic type you pass is the same as the _Action Type_ in the Input Action Map.

    I alluded to Unity Messages in the [section on Components](https://blog.eyas.sh/2020/10/unity-for-engineers-pt1-basic-concepts#component) in this series. We already saw message functions such as `Update()`, `OnEnable()`, and others. Here, the component calls messages that any other component in this GameObject can implement.

2. **Broadcast Messages**: The exact behavior discussed above, but using [`BroadcastMessage`](https://docs.unity3d.com/ScriptReference/Component.BroadcastMessage.html) instead.

    All `MonoBehaviour`s receive a broadcasted message on the GameObject _or any of its child objects_.

3. **Invoke Unity Events**: Exposes a `UnityEvent<InputAction.CallbackContext>` for each action in the asset. [UnityEvents](https://docs.unity3d.com/Manual/UnityEvents.html) have a friendly API _and_ Editor interface and allow you to specify the exact set of methods to trigger during an action.

    [`InputAction.CallbackContext`](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.InputAction.CallbackContext.html) is also richer than `InputValue`, exposing the corresponding InputAction. Further details such as whether the event corresponds to an action that was `canceled` (e.g., a button that was released), `performed`, or `started` (e.g., a button that was just pressed). Like `InputValue`, the Callback Context exposes a method to read its value: `TValue ReadValue<TValue>()`.

    A Unity Event is triggered every time an action is _started_, _performed_, and _canceled_. While an InputAction is _actuated_, it will trigger a _performed_ event each time the underlying value changes.

    An action such as a digital button will typically fire a single _started_, _performed_, and _canceled_ event. Holding a button for 5 seconds, for example, would merely delay the _canceled_ event.

    On the other hand, a more variable value, such as a gamepad stick `Vector2` value or a Mouse delta `Vector2`, would trigger a _started_, typically followed by many _performed_ events, and finally, a _canceled_ event.

4. **Invoke C# Events**: Exposes an `onActionTriggered` [C# Event](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/event) on the component which similarly triggers an `InputAction.CallbackContext`, as described above.

    Unlike Unity Events, C# events cannot be modified from the editor. Therefore, components that want to subscribe to an event will need to reference the PlayerAction component to subscribe to the event. To avoid leaking memory, you should always remember to subscribe in `OnEnable` and unsubscribe in `OnDisable`.

    Also, unlike Unity Events, there is only a single `onActionTriggered` event that will trigger the callback for any action that takes place. This means your callback should switch/condition on the Callback Context's `action` property.

### Using Unity Events

I usually prefer using Unity Events with the Input System for a few reasons:

-   I find the `InputAction.CallbackContext` API more helpful than `InputValue`;
-   In `SendMessage` and `BroadcastMessage`, a typo in the message name fails silently, resulting in a method that never gets called;
-   Compared to C# events, I find it helpful to visually separate the handlers for each action, not only across methods in the same component but across components (e.g., a Move component and an Attack component).

{% youtube TG2dWXlHrtI%}

A Unity Event can trigger callbacks on any Unity Object, whether in Asset form (e.g., a `ScriptableObject`) or as a Scene GameObject[^2]. In most cases, you will probably want to be passing your events to a `MonoBehaviour` on a _scene object_. Make sure to select the object in the _"Scene"_ tab rather than
_"Assets"_ (i.e., do not select a prefab)[^3].

[^2]:
    Read more about the difference in the first installment, [Basic Concepts in Unity for Software Engineers](https://blog.eyas.sh/2020/10/unity-for-engineers-pt1-basic-concepts).

[^3]:
    For example, if you want to move your player, you should pass the Player from the scene tabs. If your player is prefab-ed and you pass the prefab, you'll see some unexpected behavior when trying to move the player.

Therefore, the typical process will be to

-   select the _scene object_ that contains the component you want to call,
-   select the sub-menu corresponding to the specific component, and
-   pick your method from the menu of available public methods.

In cases where your public method signature takes in a single
`InputAction.CallbackContext` parameter, it will show up in the _"Dynamic CallbackContext"_ section, and automatically bind that input when the event triggers.

### Inputs and Frame-rate Independence

Unity will call the `Update` method as often as it can. Each update method represents a single _frame_ in your game. This means that you should not use a constant "speed per frame" to control _how much the player moves per frame_, for example, because that will alter the player's apparent speed as the frame rate fluctuates.

Unity exposes `Time.deltaTime` largely for this purpose, which gives you the elapsed time since the last frame. If you want your player to move by a constant speed, for example, you will want to make sure to multiple by `Time.deltaTime`:

```cs
Vector3 moveVelocity = _moveSpeed * (
    _currentMove.x * Vector3.right +
    _currentMove.y * Vector3.forward
  );
Vector3 moveThisFrame = Time.deltaTime * moveVelocity;

transform.position += moveThisFrame;
```

_With Unity Events_, a common way to handle button movement is to do something like this:

```cs
using UnityEngine;
using UnityEngine.InputSystem;

namespace Game.Controls
{
  public class PlayerMover : MonoBehaviour
  {
    [SerializeField] private float _moveSpeed;
    private Vector2 _currentMove;

    public void OnMove(InputAction.CallbackContext context)
    {
      // This returns Vector2.zero when context.canceled
      // is true, so no need to handle these separately.
      _currentMove = context.ReadValue<Vector2>();
    }

    private void Update()
    {
      Vector3 moveVelocity = _moveSpeed * (
          _currentMove.x * Vector3.right +
          _currentMove.y * Vector3.forward
        );
      Vector3 moveThisFrame = Time.deltaTime * moveVelocity;
      transform.position += moveThisFrame;
    }
  }
}
```

Frame-rate independence is also helpful with action cooldowns (e.g., not being able to Fire multiple times too frequently), changing rotations, etc. But you should also watch for pitfalls of when _not_ use `Time.deltaTime` in a computation.

A great example to watch out for is **mouse delta**. Mouse Delta tells you how much a mouse has moved.

If you have a pointer to an `InputAction` in your `Update` method, the `Vector2` returned from a mouse delta action will give you the total distance moved this frame. A longer frame will have a larger value (in other words, `Time.deltaTime` is already accounted for), and multiplying by `Time.deltaTime` will further cause the value to be disproportionately larger for longer frames, and disproportionately smaller for shorter frames.

If you are using UnityEvents, a mouse delta action will result in your callback potentially being called multiple times per frame with a different delta value. These are separate deltas that need to compose additively. Suppose you want to handle player movement and rotation (or camera rotation) only in `Update()`, `LateUpdate()`, or `FixedUpdate()`. In that case, you'll need to keep track of the delta cumulatively for each frame, resetting it between frames. For example:

```cs
using UnityEngine;
using UnityEngine.InputSystem;

namespace Game.Controls
{
  /// <summary>
  /// Moves the object according to input. Typically attached to a top-down
  /// camera overlooking the scene.
  /// </summary>
  public class ViewportMover : MonoBehaviour
  {
    [SerializeField] private float _moveSensitivity;
    private Vector2 _panThisFrame;

    public void OnMove(InputAction.CallbackContext context)
    {
      // This works for delta values, since it is reset at the end of each
      // frame.
      //
      // If the game was cross-platform, and other panning controls used
      // console sticks, for example, then those events should replace
      // each others, rather than add. We can check context.control.
      _panThisFrame += context.ReadValue<Vector2>();
    }

    private void Update()
    {
      // For Mouse delta, _panThisFrame is already frame-rate independent, and
      // only needs to be multiplied by a sensitivity value.
      //
      // If the game was cross-platform, we'd need to multiply this value when
      // using a non-delta control. We can check context.control.
      Vector3 panThisFrame = _moveSensitivity * (
          _panThisFrame.x * Vector3.right +
          _panThisFrame.y * Vector3.forward
        );

      transform.position += panThisFrame;

      // Reset pan at the end of each frame.
      _panThisFrame = Vector2.zero;
    }
  }
}
```

## Final Thoughts

There is much to learn when mastering smooth player inputs, but I hope the resources above give you the tools you need to start. Whether it is thinking about frame-rate independence (and knowing when not to blindly multiply by `Time.deltaTime`), creating easily overridable player inputs using Input Action Maps, or cross-platform input, I hope input in Unity feels less mysterious by
now.

I also hope I convinced you that Unity Events are a great way to substitute hard cross-component references (statics or using `GetComponent`) into injectable connections via the Editor. Keep them in mind next time you design a system that needs to interact across components.