Skip to content

Game.UI.Menu.InputRebindingUISystem

Assembly: Game
Namespace: Game.UI.Menu

Type: class

Base: UISystemBase

Summary:
InputRebindingUISystem manages interactive input rebinding from the UI. It hooks into Unity's InputSystem rebinding operations to capture primary control and modifier inputs, computes conflicts with existing bindings (including linked actions and aliases), and exposes conflict information to the UI so the user can choose how to resolve conflicts (swap, unset, or leave unsolved). It temporarily blocks input via Game.Input.InputManager while rebinding, supports keyboard / mouse / gamepad rebinding flows (including modifier handling), and applies binding changes with Game.Input.InputManager.DeferUpdating to batch updates safely.


Nested types

  • private enum Options
    Flags enum used internally when computing rebind resolution options (None, Unsolved, Swap, Unset, Forward, Backward).

  • private record BindingPair(ProxyBinding oldBinding, ProxyBinding newBinding)
    Simple pair used when collecting and processing candidate binding swaps.

  • private struct ConflictInfo : IJsonWritable
    Data structure sent to UI representing an active rebind target and the conflicts discovered for it. Fields include:

  • ProxyBinding binding — the binding the user is trying to apply.
  • ConflictInfoItem[] conflicts — conflict entries, usually filtered for visible ones.
  • bool unsolved, bool swap, bool unset, bool batchSwap — boolean flags describing available resolution strategies.
  • Write(IJsonWriter writer) — serializes this struct for UI JSON binding.

  • private struct ConflictInfoItem : IJsonWritable
    Individual conflict entry:

  • ProxyBinding binding — existing binding that would be affected.
  • ProxyBinding resolution — automatic resolution candidate (swap/unset etc).
  • Options options — computed options for this conflict.
  • bool isAlias, bool isHidden — metadata for UI.
  • Write(IJsonWriter writer) — serializes this item for UI JSON binding.

Fields

  • private const string kGroup = "inputRebinding"
    Group name used when registering UI bindings.

  • private ValueBinding<ProxyBinding?> m_ActiveRebindingBinding
    ValueBinding exposing the currently active rebinding target to UI (null when none).

  • private ValueBinding<ConflictInfo?> m_ActiveConflictBinding
    ValueBinding exposing conflict information for the active rebinding to UI (null when none).

  • private InputActionRebindingExtensions.RebindingOperation m_Operation
    The primary InputSystem rebinding operation used to capture the main control press.

  • private InputActionRebindingExtensions.RebindingOperation m_ModifierOperation
    A separate rebinding operation used to detect modifier keys/buttons pressed together with the main control.

  • private ProxyBinding? m_ActiveRebinding
    The binding currently being rebinding (nullable).

  • private Action<ProxyBinding> m_OnSetBinding
    Callback invoked when a new binding is applied (calls into code that persists the new ProxyBinding).

  • private ProxyBinding? m_PendingRebinding
    If conflict resolution is required, the new binding is stored here until the user resolves and completes the rebind.

  • private Dictionary<string, ConflictInfoItem> m_Conflicts
    Internal map of conflict entries keyed by title (used while computing and presenting conflict options).

Notes: - Several private helper collections and temporaries are used during conflict computation (see nested types and methods). - The class registers to InputSystem.onDeviceChange and must be disposed/unregistered on destroy.


Properties

  • This class exposes no public properties. UI state is provided via ValueBinding instances registered on OnCreate (m_ActiveRebindingBinding / m_ActiveConflictBinding).

Constructors

  • public InputRebindingUISystem()
    Default constructor. The system is fully initialized in OnCreate (bindings, rebinding operations, InputSystem device change subscription).

Methods

  • protected override void OnCreate()
    Initializes the system: registers ValueBindings and TriggerBindings with the UI group, initializes m_Operation and m_ModifierOperation and their callbacks (OnApplyBinding, OnComplete, OnCancel, OnModifierPotentialMatch, OnModifierApplyBinding). Subscribes to InputSystem.onDeviceChange. This is where UI tokens like "inputRebinding/activeRebinding" and "inputRebinding/activeConflict" are registered.

  • protected override void OnDestroy()
    Disposes rebinding operations, unsubscribes from InputSystem.onDeviceChange and calls base.OnDestroy.

  • private void OnDeviceChange(InputDevice changedDevice, InputDeviceChange change)
    Handles device added/removed events. If a matching device for the active rebinding is no longer present, it will cancel the active rebind to avoid stale rebinding state.

  • public void Start(ProxyBinding binding, Action<ProxyBinding> onSetBinding)
    Begins an interactive rebinding of the specified ProxyBinding. If the binding is keyboard/mouse/gamepad, configures m_Operation and m_ModifierOperation paths and exclusions appropriately. Temporarily sets Game.Input.InputManager.instance.blockedControlTypes to avoid interfering input. Updates the active binding ValueBinding for UI and clears prior conflicts.

  • Parameters:

    • binding — the ProxyBinding to rebind.
    • onSetBinding — callback invoked when the final binding is applied.
  • public void Start(ProxyBinding binding, ProxyBinding newBinding, Action<ProxyBinding> onSetBinding)
    Alternate Start overload used to programmatically propose a newBinding for "binding" (e.g., when a target is selected from UI rather than captured from devices). This performs processing of conflicts immediately via Process(...) and will either apply or present conflicts.

  • public void Cancel()
    Cancels any ongoing interactive rebinding operation (resets m_Operation and internal state).

  • private void CompleteAndSwapConflicts()
    Completes a pending rebind and resolves conflicts by swapping conflicting bindings to the proposed resolutions. Uses Game.Input.InputManager.DeferUpdating() to batch SetBindings calls, then applies the pending rebinding.

  • private void CompleteAndUnsetConflicts()
    Completes a pending rebind and resolves conflicts by unsetting conflicting bindings (clearing the path and modifiers). Also batches updates and applies the pending rebinding.

  • private void OnApplyBinding(InputActionRebindingExtensions.RebindingOperation operation, string path)
    Called by m_Operation when a candidate main control is detected. Creates a new ProxyBinding from m_ActiveRebinding with the detected path and (if applicable) modifiers from m_ModifierOperation candidates. Calls Process(...) to compute conflicts or apply immediately.

  • private void OnComplete(InputActionRebindingExtensions.RebindingOperation operation)
    Called when the primary rebinding operation completes. If there is no pending rebinding requiring user resolution, it resets internal state.

  • private void OnCancel(InputActionRebindingExtensions.RebindingOperation operation)
    Called when the primary rebinding operation is canceled — clears blocked control types and resets internal state.

  • private void Process(ProxyBinding oldBinding, ProxyBinding newBinding)
    Central logic for deciding whether the new binding can be applied immediately or whether the user must be asked to resolve conflicts. Steps:

  • Log attempted rebind.
  • If newBinding.action is null -> reset.
  • If NeedAskUser(...) is false -> Apply(newBinding) and Reset.
  • Otherwise gather conflicts (m_Conflicts.Clear(); GetRebindOptions(...)). If no conflicts -> Apply and Reset. If conflicts exist -> store newBinding in m_PendingRebinding and update m_ActiveConflictBinding with a ConflictInfo containing conflicts and available options.

Internal helper NeedAskUser(binding) checks linked actions, aliases shown in options, and binding.hasConflicts to determine whether to prompt user.

  • private void GetRebindOptions(Dictionary<string, ConflictInfoItem> conflictInfos, ProxyBinding oldBinding, ProxyBinding newBinding, out bool unsolved, out bool batchSwap, out bool swap, out bool unset)
    High-level orchestrator that builds a map of which bindings must change if the proposed binding is applied. It collects linked bindings with CollectLinkedBindings, then iterates the rebinding map to compute possible conflict resolutions (swap, unset, or mark unsolved). Sets the out flags to help the UI choose which resolution buttons should be enabled.

  • private void GetRebindOptions(Dictionary<string, ConflictInfoItem> conflictInfos, List<BindingPair> list)
    Internal overload that attempts to compute forward/backward propagation of swaps by analyzing usages and potential conflicts. This performs iterative processing calling ProcessConflict to propagate swap/unset decisions.

  • private void CollectBindingConflicts(List<BindingPair> conflicts, ProxyBinding toCheck, ProxyBinding resolution)
    Searches InputManager.instance.keyActionMap for matches to toCheck.path and adds any conflicting bindings from other actions that share the same control path and device. Also handles linked actions by adding corresponding derived pairs that must move with linked composites.

  • private bool CollectLinkedBindings(ProxyBinding oldBinding, ProxyBinding newBinding, out Dictionary<ProxyBinding, List<BindingPair>> rebindingMap)
    Starts from the old binding and walks its ProxyAction.m_LinkedActions to collect any other bindings that must be updated when this action is rebound (e.g., composite parts of linked actions). Returns false when a linked binding is not rebindable, causing the overall rebind to be blocked.

  • private void CollectAliases(Dictionary<string, ConflictInfoItem> conflictInfos, ConflictInfoItem mainInfo)
    Adds alias (UI alias) variants of a ConflictInfoItem so the UI can display conflicts for each shown alias and keep title uniqueness. Also maps non-alias versions back to alias data (so users see all affected UI aliases).

  • private void ProcessConflict(Dictionary<string, ConflictInfoItem> conflictInfos, List<BindingPair> bindingConflicts, ref Usages usages, ref Usages otherUsages, Options direction, out bool changed)
    Given a list of binding conflicts and the current usages context, this method classifies each conflict as Swap, Unset, or Unsolved (and adds them to conflictInfos), updates Usages to propagate changes, and removes processed pairs from bindingConflicts. Used in the forward/backward iterative resolution algorithm.

  • private static bool CanSwap(ProxyBinding x, ProxyBinding y, bool checkUsage)
    Helper returning whether binding x can be swapped with binding y given rebindability, set state, path equality, modifiers compatibility and (optionally) usage overlap constraints.

  • private void Apply(ProxyBinding newBinding)
    Applies the binding by invoking m_OnSetBinding under a Game.Input.InputManager.DeferUpdating() scope to ensure batch updates.

  • private void Reset()
    Clears UI ValueBindings, resets m_ModifierOperation, clears m_ActiveRebinding / m_OnSetBinding / m_PendingRebinding and m_Conflicts.

  • private void OnModifierPotentialMatch(InputActionRebindingExtensions.RebindingOperation operation)
    Callback for modifier operation potential match — no behavior implemented in this class (stub).

  • private void OnModifierApplyBinding(InputActionRebindingExtensions.RebindingOperation operation, string path)
    Callback when a modifier candidate is applied — stub in this class; modifier reading is handled in OnApplyBinding via m_ModifierOperation.candidates.


Usage Example

// Example: start an interactive rebinding for a ProxyBinding and apply new binding via callback.
//
// Assuming you have a ProxyBinding 'binding' (the current binding you want to rebind) and the
// InputRebindingUISystem instance has been created/registered by the UI system:

inputRebindingUISystem.Start(binding, newBinding =>
{
    // persist the new binding (Apply will call this under DeferUpdating)
    // e.g. InputManager.instance.SetBinding(newBinding) or call your own persistence logic.
});

Notes and tips for modders: - The system exposes two ValueBindings ("inputRebinding/activeRebinding" and "inputRebinding/activeConflict") which the UI reads to show the active rebinding state and conflict list. ConflictInfo and ConflictInfoItem implement IJsonWritable and are intended to be serialized to UI JSON. - When rebinding is active the system temporarily sets Game.Input.InputManager.instance.blockedControlTypes to the relevant device type (Keyboard/Mouse/Gamepad/None) to avoid accidental action triggers. - Conflict resolution supports three main flows: - "Swap": automatically swap the conflicting binding to the new path (may require moving linked bindings). - "Unset": clear conflicting binding(s). - "Unsolved": user must decide because the binding cannot be safely moved. - Changes are applied inside Game.Input.InputManager.DeferUpdating() to avoid intermediate inconsistent InputManager state and to batch multiple SetBindings calls. - The class subscribes to InputSystem.onDeviceChange; if a relevant device is removed during rebinding the operation is canceled to avoid an inconsistent state.

If you want a tailored example for hooking this into your modded UI (e.g., connecting the ValueBindings to your UI panel), tell me which UI framework you use and I can provide the binding code.