Binding conflicts
A binding conflict is when an action with more than one control bound to it receives values from multiple controls.
Conflict situations
Binding conflict situations can arise in two different ways. Either:
- Multiple concurrent controls. Several controls are bound to the same action and more than one is feeding input into the Action at the same time. Example: an Action that is bound to both the left and right trigger on a Gamepad and both triggers are pressed.
Or:
- Multiple input sequences. The input is part of a sequence of inputs and there are several possible such sequences. Example: one Action is bound to the
Bkey and another Action is bound toShift-B.
Conflict Resolution
For value and button type actions, the Input System continuously monitors all the controls bound to the action, and then chooses the one which is the most actuated to be the control driving the action. Most actuated means the largest absolute value is being reported, whether positive or negative in the case of a 1D axis, and regardless of direction in the case of 2D and 3D axes. The value of the most actuated control is reported in callbacks, and triggered whenever the value changes.
If a different bound control is actuated more, that control becomes the control driving the action. This is useful if you want to allow different Controls to control an Action in the game, but only take input from one Control at the same time.
Pass-through type actions do not perform conflict resolution, and are intended allow multiple concurrent inputs.
Multiple concurrent controls
For a Button or Value action type, there can only be one control at any time that is "driving" the action. This control is considered the activeControl.
When an action is bound to multiple controls, the active control at any point is the one with the greatest level of "actuation" (the one with the largest value returned from EvaluateMagnitude). If a different control exceeds the actuation level of the current active control, it becomes the active control.
For composite bindings, magnitudes of the composite as a whole rather than for individual controls are tracked. However, activeControl will still track individual Controls from the composite.
Multiple input sequences (such as keyboard shortcuts)
Note
The mechanism described here only applies to Actions that are part of the same action map or action assets.
Inputs used in combinations with other inputs can also lead to ambiguities. If, for example, the B key on the Keyboard is bound both on its own as well as in combination with the Shift key, then if you first press Shift and then B, the latter key press would be a valid input for either of the Actions.
To decide which action responds, enable one of the settings Edit > Project Settings > Input System Package > Settings > Improved Shortcut Support. For more information, refer to Improved Shortcut Support settings.
Resolve shortcuts with action priority
To choose which action responds, enable Priority Consumption (InputSettings.shortcutKeysUseActionPriority) and set each action's Priority. The Input System notifies higher-priority actions first. When an action with a priority greater than 0 performs, it consumes the input, so lower-priority actions on the same control don't respond.
In the example, give the Shift + B action a higher priority than the B action. When the user presses Shift + B, only the Shift + B action responds.
For more information, refer to Overlapping bindings and action priority.
Resolve shortcuts with binding complexity
Note
Complexity Consumption exists for compatibility with projects made with earlier versions of the Input System, and its results can be hard to predict. For new projects, use Priority Consumption instead.
If you enable Complexity Consumption (InputSettings.shortcutKeysConsumeInput) and leave Priority Consumption disabled, the Input System processes bindings in order of decreasing complexity. This metric is derived automatically from the binding:
- A binding that is not part of a composite is assigned a complexity of 1.
- A binding that is part of a composite is assigned a complexity equal to the number of part bindings in the composite.
In the example, a one-modifier composite binding to Shift + B has a higher complexity than a binding to B, so the Input System processes it first.
The first binding that results in the Action changing phase consumes the input, so other bindings to the same input aren't processed. In the example, when the Shift + B binding consumes the B input, the Input System skips the binding to B.
If two composites have the same complexity, the order in which the Input System processes them isn't defined.
Disable Conflict Resolution
Conflict resolution is always applied to Button or Value action types. However, it can be undesirable in situations when an action is simply used to gather all inputs from bound Controls. For example, if you have a Button type action bound to the A button on all gamepads, a user holding down A on one gamepad means that the A button on other gamepads is ignored.
By using the Pass Through action type, conflict resolution is bypassed, which means pressing the A button on one gamepad will not result in presses on other gamepads being ignored.
API Example
The following example illustrates how this works at the API level.
// Resolve overlapping bindings by action priority.
InputSystem.settings.shortcutKeysUseActionPriority = true;
// Create two actions in the same map.
var map = new InputActionMap();
var bAction = map.AddAction("B");
var shiftbAction = map.AddAction("ShiftB");
// Bind one of the actions to 'B' and the other to 'SHIFT+B'.
bAction.AddBinding("<Keyboard>/b");
shiftbAction.AddCompositeBinding("OneModifier")
.With("Modifier", "<Keyboard>/shift")
.With("Binding", "<Keyboard>/b");
// Give SHIFT+B a higher priority than B so that it consumes the B press.
shiftbAction.Priority = 10;
// Print something to the console when the actions are triggered.
bAction.performed += _ => Debug.Log("B action performed");
shiftbAction.performed += _ => Debug.Log("SHIFT+B action performed");
// Start listening to input.
map.Enable();
var keyboard = Keyboard.current ?? InputSystem.AddDevice<Keyboard>();
// The left shift key on the keyboard is pressed (here, we manually
// press it by queueing a state event for the keyboard).
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.LeftShift));
InputSystem.Update();
// And then the B is pressed. This is a valid input for both
// bAction as well as shiftbAction.
//
// shiftbAction, which has the higher priority, processes first.
// In response, it performs the action (the `performed` callback being invoked)
// and consumes the input. bAction stays silent because it's skipped.
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.LeftShift, Key.B));
InputSystem.Update();