Skip to main content
Version: Next (dev)

Input Action Maps & Rebinding

An Input Action Map describes what the player can do (actions) and which keys, buttons and sticks do it (bindings). Actions are grouped into contexts such as Gameplay, Vehicle or UI, and contexts can be switched on and off at runtime. Players can rebind any binding marked as rebindable. Their changes are saved per user, layered on top of the asset, and survive later updates to the asset.

The map sits on top of the input slot layer. Devices, slot names such as keyboard_w or controller0_button_a, and dead zones on slots work as before. Legacy input sets from RuntimeConfigs/InputConfig.ddl keep working alongside a map.

Concepts​

Contexts​

A context is a group of actions with a priority. Contexts are evaluated from the highest priority to the lowest. When a context has Consume Input enabled and is active, every slot it binds is hidden from lower-priority contexts. For example, a UI context with priority 100 that binds keyboard_space to Submit stops Space from also making the character jump. Active By Default decides whether the context starts enabled.

An active UI context at priority 100 consumes Space for Submit, hiding it from Jump in the lower-priority Gameplay context.

Context-priority diagram. Only slots bound by the active consuming context are hidden from lower-priority contexts.

Actions​

An action has a name, an optional display name and a value type. The value is always a 3D vector, and the value type decides which components are used.

Value typeValueTypical use
Buttonx = strength of the strongest bindingJump, Fire, Interact
Axis1Dx in [-1, 1]Throttle, Zoom
Axis2Dx, yMove, Look, UI navigation
Axis3Dx, y, zFree-fly camera

Bindings​

A binding connects slots to an action. Its kind decides how many slots it uses:

KindSlots (in order)Example
Simpleone slotkeyboard_space
Composite1Dnegative, positivekeyboard_s, keyboard_w
Composite2D-x, +x, -y, +yA, D, S, W or a thumbstick's four half-axes
Composite3D-x, +x, -y, +y, -z, +zWASD + Q/E

Modifiers are extra slots that must be held for the binding to work, such as Ctrl for Ctrl + S. While a binding with modifiers is active, plain bindings on the same slot in the same context are ignored, so Ctrl + S saves without also moving the character backwards.

Gamepad slots are written without a controller number, for example controller_button_a. Such a slot follows the player's controller: it means controller0_button_a for player 0 and controller1_button_a for player 1. The editor lists gamepad slots only in this form. A numbered slot such as controller2_button_a can still be typed in and always means that one physical controller. Importing InputConfig.ddl turns controller0_ slots into the player form.

Processors​

Processors change a binding's value in the order they are listed. Axes limits Invert, Scale, Sensitivity, Clamp and Power to the selected axes.

ProcessorValue / Value2Effect
DeadZonelower / upperRadial: length below lower becomes 0 and length above upper becomes 1, and values in between are remapped. The user value DeadZone.<Scheme> replaces lower.
Invertn/aNegates the axes.
ScalefactorMultiplies the axes.
SensitivityfactorLike Scale, and also multiplied by the user value <Action>.Sensitivity.
Normalizen/aLimits the vector length to 1 (prevents diagonals from moving faster).
Clampmin / maxClamps the axes.
Powerexponentsign(v) · abs(v)^exponent, for response curves.

Triggers​

A binding counts as actuated while the length of its value is at least the trigger's Threshold. The trigger type decides on which frames the action is reported as triggered.

TriggerTriggeredUses
Presson the frame it becomes actuatedn/a
Downevery frame while actuatedn/a
Releaseon the frame it stops being actuatedn/a
Holdonce, after being actuated for TimeTime
Tapon release, if held for at most TimeTime
DoubleTapon a second press that starts within Time of the first releaseTime
Repeaton press, then every Repeat Interval after the initial delay TimeTime, Repeat Interval

Besides the trigger, each action reports a key state (Up, Pressed, Down, Released) that follows actuation. It also reports a phase (Started, Ongoing, Triggered or Canceled) for the binding that currently drives it. A Hold released too early ends as Canceled.

Schemes​

Each binding belongs to a scheme: Keyboard & Mouse, Gamepad, XR or Touch. The scheme is usually derived from the slot name. The engine tracks which scheme the player used last, and button prompts and the Controls menu use it.

Setting up an input action map​

  1. In the asset browser, create an Input Action Map asset. Project › Input Configuration offers to create one, importing the project's existing InputConfig.ddl.
  2. Add contexts, then add actions to each context and choose their value types.
  3. Select an action, add bindings, and click a slot to capture a key or mouse button. Press Esc to cancel the capture.
  4. Set triggers and processors in the property grid. Clear Rebindable on bindings that players must not change, such as the pause menu key.
  5. Click Validate to find duplicate slots within a context.
  6. Reference the asset in the project's startup settings (Startup.ddl, entry InputActionMap). The Player and Play-in-Editor activate it for player 0 at startup.
note

Each simple binding without modifiers is also written into a legacy input set with the context's name, up to three per action. Code that calls plInputManager::GetInputActionState("Gameplay", "Jump") therefore sees the map's bindings too.

Reading input​

Input Component​

Set the component's Input Context to a context of the map. The component then sends plMsgInputActionTriggered for that context's actions. The message has Value (vec3), Triggered and TriggerPhase, alongside KeyPressValue and TriggerState. A message is also sent on every frame where a trigger fires, for example when a hold completes. Forward To Blackboard writes a float for Button and Axis1D actions, a vec2 for Axis2D and a vec3 for Axis3D.

PropertyDefaultDescription
Input Setn/aLegacy input set. Used when Input Context is empty.
Input Contextn/aContext in the player's action map.
Player0Which player's map to read (0–3).
GranularityPressOnlyWhich key-state changes send messages.
Forward To BlackboardoffMirrors action values into the owner's blackboard.

Scripts​

AngelScript and Visual Script expose the Input class:

Input::GetActionValue("Move") // vec3
Input::GetActionAxis("Throttle") // float
Input::IsActionDown("Sprint")
Input::WasActionPressed("Jump")
Input::WasActionTriggered("Interact") // respects Hold / Tap / DoubleTap
Input::PushContext("Vehicle") // applied at the next input update
Input::PopContext("Vehicle")
Input::GetActionPrompt("Interact") // "E" or "X" depending on the active scheme

C# uses Plasma.InputBindings. C++ uses plInputActionMap::GetPlayer(0).GetAction("Move").

Runtime rebinding​

plGameApplication::GetInputBindings() owns player 0's map and the user's changes. The game UI Controls menu and C# use its Execute command interface:

CommandArgumentsResult
QueryScheme?Actions (name, display name, context, bindings with slot labels, Primary / Secondary prompts), Values
BeginRebindAction, Binding or BindingIndex, Scheme, SlotIndex, Conflict, TimeoutStarts listening
Polln/aRebind: Status, SlotLabel, ConflictAction, SecondsRemaining
ResolveResolution = Swap | Replace | RejectFinishes a rebind that stopped on a conflict
Cancel / Reset / SaveReset: Action?n/a
SetValueKey, ValueTuning values, such as Look.Sensitivity.Gamepad or Look.InvertY
PromptAction, Scheme?Prompt

How a rebind works​

  1. When listening starts, every input that is already held is ignored until it has been released.
  2. The next key or button press, or an axis pushed past the threshold, is captured. The cancel keys (Esc and the gamepad Menu button by default) end the rebind without changes.
  3. If another rebindable binding of the same scheme in the same context already uses that slot, the Conflict rule applies. Ask stops and waits for Resolve. Swap gives the other binding this binding's old slot. Replace removes the slot from the other binding.
  4. The captured key does not trigger its new action until it has been released once.
  5. The change is saved on the next frame.

While a rebind is active, every action reports released.

Where changes are stored​

Changes are saved to <appdata>/<project>/Settings/<user>/InputBindings.ddl, next to GameSettings.ddl, with an InputBindings.last-good.ddl backup. When the signed-in platform user changes, the file of the new user is loaded. The file stores only the differences from the asset, keyed by binding and action GUIDs. Renaming an action in the asset therefore keeps the player's bindings, while deleting a binding drops its override.

Button prompts​

Prompt labels come from :base/RuntimeConfigs/InputGlyphs.ddl, then :project/RuntimeConfigs/InputGlyphs.ddl:

Glyph { string %Slot{"controller_button_a"} string %Label{"A"} string %Sprite{"UI/Glyphs/XboxA"} }

Slots without an entry use the display name that the device registered. Composite bindings are shown in W/A/S/D order, and modifiers as Ctrl + S.

Troubleshooting​

SymptomCause
An action never firesIts context is not active. Check Active By Default and your PushContext calls. Also check whether a higher, consuming context binds the same slot.
A gameplay action fires while a menu is openThe UI context does not consume input, or it does not bind that slot.
A stick action jitters around zeroAdd a DeadZone processor, or raise the trigger threshold.
A rebind never completesThe key is excluded, belongs to another scheme than the request, or conflicts with Reject.
Rebinds are lost after an updateThe binding was deleted and recreated in the asset, which gives it a new GUID.
"deserialized A of B bytes" for plInputComponentThe asset cache is stale after the component moved to version 4. Delete the project's AssetCache.