Input System architecture
Understand how the low-level and high-level systems of the Input System turn platform input into actions that your application responds to.
The Input System consists of a low-level system and a high-level system:
- The low-level system receives raw input from the platform-specific back end that ships with Unity. The system stores that input as device state in unmanaged memory.
- The high-level system interprets that state as controls, actions, and bindings that your application code responds to.
The built-in back end supplies the input, the low-level system stores it, and the high-level system interprets it.
The built-in back end
The built-in back-end is platform-specific code that collects information about the available devices and the input data from those devices. The code isn't part of the Input System package. It ships with Unity itself, and it has an implementation for each runtime platform that Unity supports.
The Input System interfaces with the built-in back end in two ways:
- The built-in back end sends events to the Input System. These events tell the Input System when the platform adds or removes an input device and when a device's state changes. For efficiency, the built-in back end reports these events as a simple buffer of raw, unmanaged memory that contains a stream of events.
- The Input System sends commands to devices in the built-in back end. Commands are buffers of memory that the built-in back end interprets. A command can have a different meaning for each device type and platform.
Diagram colors and shapes
The diagrams on this page use both color and shape to show what each node represents, so you can tell the node types apart without relying on color. The following table describes each combination:
| Color | Shape | Represents |
|---|---|---|
| Yellow | Parallelogram | Platform back ends and the runtime events, queues, and update calls that carry input between them and the Input System. |
| Teal | Trapezoid, wider at the top | Background threads that queue events from outside the main thread. |
| White with a red border | Rectangle with double vertical sides | The InputManager class. |
| Blue | Rounded rectangle | Layouts. |
| Blue | Circle | The reusable control building blocks that layouts are made from. |
| Green | Rectangle | Devices and their controls. |
| Purple | Cylinder | Input state memory. |
| Light purple | Pill shape | The InputActionState object and the arrays it holds. |
| Light purple | Hexagon | The state change monitors that the InputActionState object registers. |
| Orange | Trapezoid, wider at the bottom | Action assets, action maps, actions, and bindings. |
| Gray | Double circle | Scene GameObjects. |
| Gray | Flag | The nodes that link one diagram to the next. |
| White | Rectangle with a dashed gray border | Supporting objects outside the main input path, such as InputUser. |
The low-level system
The low-level Input System code processes and interprets the memory in the event stream that the built-in back end provides. It then dispatches individual events.
When the Input System discovers a device in the event stream, it creates a device representation for that device. The low-level code sees a device as a block of raw, unmanaged memory.
When the low-level code receives a state event for a device, it writes the data from that event into the device's state representation in memory. The state therefore always holds an up-to-date representation of the device and all its controls.
The low-level system code also contains structs that describe the data layout of commonly known devices.
The low-level system works as a layered pipeline. The following three diagrams each show one stage of that pipeline:
- The platforms' back ends queue events for the
InputManagerclass. - The
InputManagerclass uses layouts to build devices. - Each device writes its state into input state memory.
Note
InputManager is the Input System class that owns devices and drives updates. It's not the Input Manager section of the Project Settings window, which is unrelated to the Input System package.
Diagram 1: Platform back ends queue events
This diagram shows the following processes:
- The built-in back ends push discovery and state events into the device discovery queue and the foreground event queue, which drive the
InputManagerclass. - Events that come from background threads go into the background event queue, which the main thread flushes into the foreground queue.
- The
InputManagerclass sends commands back to the back ends.
Diagram 2: Layouts build devices
Layouts derive from one another. Together with reusable control building blocks, they describe how to build devices and their controls. The InputManager class searches these layouts and creates the concrete devices.
The following examples show how layouts and building blocks relate to one another:
- The
Mouse,Pen, andTouchscreenlayouts all derive from thePointerlayout. - The
DualShock4GamepadHIDandDualSenseGamepadHIDlayouts, which support PlayStation controllers that connect as human interface devices (HIDs), both derive from the sharedDualShockGamepadlayout, which itself derives fromGamepad. - Building blocks such as
Stick,Axis,Button, andDpadsupply the individual controls that each device exposes.
Diagram 3: Devices store their state in memory
Each built device exposes a tree of controls. When state events arrive, the Input System writes the device's state into input state memory, where each device has its own block of unmanaged memory. Each control is a view into part of its device's block, and controls can overlap. For example, a stick's up and down controls read the same memory as its y control.
The following examples show how devices expose controls:
- A
Gamepaddevice exposes aleftStickcontrol that resolves to thex,y,up,down,left, andrightcontrols. Theupanddowncontrols share the state ofy, andleftandrightshare the state ofx. - A
Keyboarddevice exposes one control for each key.
Both devices write into the same input state memory.
The high-level system
The high-level Input System code uses layouts to interpret the data in a device's state buffers. Layouts describe a device's data and its controls in memory.
The Input System creates layouts from the predefined structs of commonly known devices that the low-level system supplies, or dynamically at runtime. For example, the Input System creates layouts at runtime for generic HIDs.
Based on the information in the layouts, the Input System creates a representation for each of the device's controls. You can then read the state of each control individually.
With the high-level system, you can also do the following:
- Map controls to your application's mechanics. Use actions to bind one or more controls to an input in your application. The Input System monitors these controls for state changes, and notifies your application logic through callbacks.
- Specify more complex behaviors for your actions. Processors transform the input data before the Input System sends it to you, and interactions let you specify patterns of input on a control to listen for, such as multi-taps.
Two diagrams describe the high-level system. The first diagram shows how input flows through the system at runtime. The second diagram shows how you create actions as assets.
Both diagrams show a single player. For each additional player, the Input System creates the following objects:
- An
InputActionStateobject for that player. - A cloned
InputActionAssetobject with its own device list and binding mask.
Diagram 4: Runtime input flow
At runtime, input reaches your scene in four steps:
- The Input System writes a device's control state into input state memory.
- A state change monitor notices the change.
- The Input System updates the
InputActionStateobject. - The resulting action fires a callback on the
PlayerInputcomponent in the scene.
The following diagram traces those steps for a single control, the Space key on a keyboard:
The keyboard's Space control stores its value as one bit of
KeyboardState.A
StateEventobject carrying that state feedsInputManager.OnUpdate(), which writes the new state into input state memory.A state change monitor on the space control detects the change and calls
NotifyControlStateChanged()on theInputActionStateobject, which processes the change inProcessControlStateChange().The
InputActionStateobject holds three arrays:actionStates[],bindingStates[], andcontrols[].The Input System fills
bindingStates[]andcontrols[]when it resolves bindings, not on every state event. Processing reads them and updates the matching entry inactionStates[].When the action changes phase, its action map raises
actionTriggered, which callsOnActionTriggered()on thePlayerInputcomponent directly.
The diagram shows the callback path when the Behavior property of the PlayerInput component is set to Send Messages, Broadcast Messages, or Invoke CSharp Events. For more information, refer to PlayerNotifications.
When Behavior is set to Invoke Unity Events, PlayerInput doesn't use OnActionTriggered(). Instead, it subscribes each action's UnityEvent directly to that action's started, performed, and canceled callbacks.
Separately, InputUser pairs devices with the PlayerInput component and associates its actions with that user. InputUser isn't part of the callback path.
Diagram 5: Action asset structure
An InputActionAsset object contains action maps, actions, and bindings. At runtime, these populate the arrays inside the InputActionState object that the previous diagram shows.
The following diagram shows an asset named MyGame.inputactions that lists Keyboard as its device and masks bindings to the KeyboardMouse group:
- The asset contains a
gameplayaction map, which contains ajumpaction and two bindings for that action:<Keyboard>/spacein theKeyboardMousegroup<Gamepad>/buttonSouthin theGamepadgroup
- The action populates
actionStates[]. - The bindings populate
bindingStates[]. - The binding states resolve to
controls[].