docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    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:

    1. The platforms' back ends queue events for the InputManager class.
    2. The InputManager class uses layouts to build devices.
    3. 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:

    1. The built-in back ends push discovery and state events into the device discovery queue and the foreground event queue, which drive the InputManager class.
    2. Events that come from background threads go into the background event queue, which the main thread flushes into the foreground queue.
    3. The InputManager class sends commands back to the back ends.
    %%{init: {"flowchart": {"nodeSpacing": 70, "rankSpacing": 110, "padding": 20, "wrappingWidth": 400}}}%% flowchart TB accTitle: Platform back ends queue events accDescr: Platform back ends and background threads queue discovery and state events, which the InputManager class consumes; InputManager sends device commands back to the back ends. %% Diagram 1: platform back ends queue events %% Keep each label line short, with explicit line breaks, so labels never wrap mid-word. backends[/"Platform back ends Windows · macOS · Linux · UWP iOS · Android · Switch · Xbox PlayStation · Web · XR"/] threads[\"Background threads Code that queues events from outside the main thread"/] %% Invisible group keeps both event sources on the top row. subgraph SRC[" "] direction LR backends threads end DDQ[/"Device discovery queue"/] BEQ[/"Background event queue"/] EQ[/"Event queue (foreground)"/] InputManager[["<b>InputManager</b> Matches layouts to devices (InputDeviceMatcher), builds devices (InputDeviceBuilder), and creates and updates them"]] backends --> DDQ backends --> EQ threads -->|"Queue event (thread-safe)"| BEQ DDQ -->|"Device discovered"| InputManager BEQ -->|"Flushed by the main thread"| EQ EQ -->|"Update (flushes event buffers)"| InputManager InputManager -->|"Queue event"| EQ InputManager -->|"Device command"| backends InputManager -.-> out1>"→ Diagram 2: layouts build devices"] classDef runtime fill:#fdf3d0,stroke:#b9962e,color:#000; classDef manager fill:#ffffff,stroke:#e0403f,stroke-width:2px,color:#000; classDef signpost fill:#f2f2f2,stroke:#999,color:#000; classDef threaded fill:#d8f0ee,stroke:#3a8a83,color:#000; class backends,DDQ,EQ,BEQ runtime; class threads threaded; class InputManager manager; class out1 signpost; style SRC fill:none,stroke:none;

    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, and Touchscreen layouts all derive from the Pointer layout.
    • The DualShock4GamepadHID and DualSenseGamepadHID layouts, which support PlayStation controllers that connect as human interface devices (HIDs), both derive from the shared DualShockGamepad layout, which itself derives from Gamepad.
    • Building blocks such as Stick, Axis, Button, and Dpad supply the individual controls that each device exposes.
    %%{init: {"flowchart": {"wrappingWidth": 400}}}%% flowchart TB accTitle: Layouts build devices accDescr: Layouts derive from one another and, with reusable control building blocks, describe how InputManager builds Gamepad and Keyboard devices. %% Diagram 2: layouts build devices %% Bands stack top-to-bottom; each band is compact left-to-right. in2>"→ from Diagram 1: the InputManager class"] -.-> InputManager InputManager[["<b>InputManager</b> Matches layouts to devices, creates them, and updates them"]] %% ---- Band A: layouts derive from one another ---- subgraph LAYOUTS["Layouts describe how to build devices"] direction LR %% Arrows point from each derived layout to the layout it derives from. Mouse("Mouse") -->|"derives from"| Pointer("Pointer") Pen("Pen") -->|"derives from"| Pointer Touchscreen("Touchscreen") -->|"derives from"| Pointer DS4_HID("DualShock4GamepadHID") -->|"derives from"| DualShock("DualShockGamepad") DualSense_HID("DualSenseGamepadHID") -->|"derives from"| DualShock DualShock -->|"derives from"| Gamepad_L("Gamepad (layout)") Keyboard_L("Keyboard (layout)") end %% ---- Band B: reusable building blocks build the controls ---- subgraph BLOCKS["Reusable building blocks build the controls"] direction LR Stick(("Stick")) -.-> leftStick["leftStick"] Axis(("Axis")) -.-> axes["x · y"] Button(("Button")) -.-> buttons["up · down left · right"] Dpad(("Dpad")) end %% ---- Band C: the devices the InputManager class creates ---- subgraph DEVICES["Devices"] direction LR Gamepad_D["Gamepad (device)"] Keyboard_D["Keyboard (device)"] end %% ---- Cross-band flow (top-to-bottom) ---- InputManager -->|"searches"| LAYOUTS LAYOUTS -.->|"build"| DEVICES BLOCKS -.->|"build the controls in"| DEVICES InputManager -->|"creates and updates"| DEVICES DEVICES -.-> out2>"→ Diagram 3: devices store state in memory"] classDef layout fill:#e6f0ff,stroke:#4a78c0,color:#000; classDef device fill:#e8f7e8,stroke:#4aa04a,color:#000; classDef manager fill:#ffffff,stroke:#e0403f,stroke-width:2px,color:#000; classDef signpost fill:#f2f2f2,stroke:#999,color:#000; class Mouse,Pen,Touchscreen,Pointer,DS4_HID,DualSense_HID,DualShock,Gamepad_L,Keyboard_L,Stick,Axis,Button,Dpad layout; class Gamepad_D,Keyboard_D,leftStick,axes,buttons device; class InputManager manager; class in2,out2 signpost; style LAYOUTS fill:#f5f9ff,stroke:#4a78c0,color:#000; style BLOCKS fill:#f5f9ff,stroke:#4a78c0,color:#000; style DEVICES fill:#f4faf4,stroke:#4aa04a,color:#000;

    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 Gamepad device exposes a leftStick control that resolves to the x, y, up, down, left, and right controls. The up and down controls share the state of y, and left and right share the state of x.
    • A Keyboard device exposes one control for each key.

    Both devices write into the same input state memory.

    %%{init: {"flowchart": {"wrappingWidth": 400}}}%% flowchart TB accTitle: Devices store their state in memory accDescr: Gamepad and Keyboard devices write their state into input state memory, where controls are views onto regions of each device's block, and some regions overlap. %% Diagram 3: devices store their state in memory %% Outer flow is top-to-bottom; subgraphs run left-to-right so controls stay readable. in3>"→ from Diagram 2: the built devices"] -.-> GP & KB subgraph GP["Gamepad (device)"] direction LR leftStick["leftStick"] --> x & y & up & down & left & right end subgraph KB["Keyboard (device)"] direction LR a b c d end StateMemory[("Input state memory Unmanaged raw memory. Each device gets a block. Controls are views onto regions of that block. Regions can overlap, such as up and down on y.")] GP -->|"writes state to"| StateMemory KB -->|"writes state to"| StateMemory classDef device fill:#e8f7e8,stroke:#4aa04a,color:#000; classDef mem fill:#e3c8ee,stroke:#8a5ea0,color:#000; classDef signpost fill:#f2f2f2,stroke:#999,color:#000; class leftStick,x,y,up,down,left,right,a,b,c,d device; class StateMemory mem; class in3 signpost; style GP fill:#f4faf4,stroke:#4aa04a,color:#000; style KB fill:#f4faf4,stroke:#4aa04a,color:#000;

    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 InputActionState object for that player.
    • A cloned InputActionAsset object with its own device list and binding mask.

    Diagram 4: Runtime input flow

    At runtime, input reaches your scene in four steps:

    1. The Input System writes a device's control state into input state memory.
    2. A state change monitor notices the change.
    3. The Input System updates the InputActionState object.
    4. The resulting action fires a callback on the PlayerInput component in the scene.

    The following diagram traces those steps for a single control, the Space key on a keyboard:

    1. The keyboard's Space control stores its value as one bit of KeyboardState.

    2. A StateEvent object carrying that state feeds InputManager.OnUpdate(), which writes the new state into input state memory.

    3. A state change monitor on the space control detects the change and calls NotifyControlStateChanged() on the InputActionState object, which processes the change in ProcessControlStateChange().

      The InputActionState object holds three arrays: actionStates[], bindingStates[], and controls[].

      The Input System fills bindingStates[] and controls[] when it resolves bindings, not on every state event. Processing reads them and updates the matching entry in actionStates[].

    4. When the action changes phase, its action map raises actionTriggered, which calls OnActionTriggered() on the PlayerInput component 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.

    %%{init: {"flowchart": {"wrappingWidth": 400}}}%% flowchart TB accTitle: Runtime input flow accDescr: A Space key press flows from a StateEvent through InputManager, a state change monitor, and InputActionState to a callback on the PlayerInput component, while InputUser handles device pairing separately. %% Diagram 4: runtime input flow %% ---------- Devices ---------- Keyboard["Keyboard"] --> space["space"] %% ---------- Input state memory ---------- KBbit[("KeyboardState 1 bit for the space key")] space -->|"stored in"| KBbit %% ---------- Runtime ---------- StateEvent[/"StateEvent (KeyboardState) from the built-in back end"/] -->|feeds| OnUpdate[/"InputManager.OnUpdate()"/] OnUpdate -->|"writes state"| KBbit %% ---------- Action state ---------- Monitor{{"State change monitor (on space)"}} KBbit -->|"change detected"| Monitor AState(["InputActionState ProcessControlStateChange()"]) Monitor ==>|"NotifyControlStateChanged()"| AState act(["actionStates[]"]) bind(["bindingStates[]"]) ctrl(["controls[]"]) bind -.->|read| AState ctrl -.->|read| AState AState ==>|updates| act %% ---------- Callback out to the scene ---------- act ==>|"InputActionMap.actionTriggered"| PI((("PlayerInput.OnActionTriggered() (scene GameObject)"))) IU["InputUser (device pairing)"] -.->|"pairs devices and actions"| PI classDef device fill:#e8f7e8,stroke:#4aa04a,color:#000; classDef mem fill:#e3c8ee,stroke:#8a5ea0,color:#000; classDef astate fill:#efe9ff,stroke:#7a5ec0,color:#000; classDef runtime fill:#fdf3d0,stroke:#b9962e,color:#000; classDef go fill:#d9d9d9,stroke:#666,color:#000; classDef support fill:#ffffff,stroke:#666,stroke-dasharray:5 4,color:#000; class Keyboard,space device; class KBbit mem; class AState,act,bind,ctrl,Monitor astate; class StateEvent,OnUpdate runtime; class PI go; class IU support;

    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:

    1. The asset contains a gameplay action map, which contains a jump action and two bindings for that action:
      • <Keyboard>/space in the KeyboardMouse group
      • <Gamepad>/buttonSouth in the Gamepad group
    2. The action populates actionStates[].
    3. The bindings populate bindingStates[].
    4. The binding states resolve to controls[].
    %%{init: {"flowchart": {"wrappingWidth": 400}}}%% flowchart TB accTitle: Action asset structure accDescr: An InputActionAsset contains an action map with a jump action and two bindings, which populate the actionStates, bindingStates, and controls arrays of InputActionState. %% Diagram 5: action asset structure %% ---------- Asset hierarchy ---------- Asset[/"InputActionAsset: MyGame.inputactions devices = [ Keyboard ] bindingMask = { groups: KeyboardMouse }"\] Map[/"InputActionMap: gameplay"\] Act[/"InputAction (m_Actions[]) gameplay/jump"\] Bind_a[/"InputBinding (m_Bindings[]) path: &lt;Keyboard&gt;/space action: jump groups: KeyboardMouse"\] Bind_b[/"InputBinding (m_Bindings[]) path: &lt;Gamepad&gt;/buttonSouth action: jump groups: Gamepad"\] Asset --> Map Map --> Act Map --> Bind_a & Bind_b %% ---------- Populates the runtime action state ---------- Act -.->|populates| act(["actionStates[]"]) Bind_a -.->|populates| bind(["bindingStates[]"]) Bind_b -.->|populates| bind bind -.->|resolves controls| ctrl(["controls[]"]) State(["InputActionState (m_State)"]) --- act & bind & ctrl classDef asset fill:#ffeede,stroke:#c07a3a,color:#000; classDef astate fill:#efe9ff,stroke:#7a5ec0,color:#000; class Asset,Map,Act,Bind_a,Bind_b asset; class act,bind,ctrl,State astate;

    Additional resources

    • Input events
    • Device commands
    • Control state
    • Layouts
    • Controls
    • Actions
    • Bindings
    In This Article
    Back to top
    Copyright © 2026 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)