From 28640ebbb82a0ce3c006a389266b854da0ab3753 Mon Sep 17 00:00:00 2001 From: Darren Kelly Date: Tue, 17 Mar 2026 14:52:25 +0100 Subject: [PATCH 01/22] Comment out the shortcutKeysConsume input setting. --- Assets/Tests/InputSystem/CoreTests_Actions.cs | 16 ++++++++-------- Assets/Tests/InputSystem/CoreTests_Analytics.cs | 6 +++--- .../Actions/Composites/ButtonWithOneModifier.cs | 2 +- .../Actions/Composites/ButtonWithTwoModifiers.cs | 2 +- .../Actions/Composites/OneModifierComposite.cs | 2 +- .../Actions/Composites/TwoModifiersComposite.cs | 2 +- .../InputSystem/Actions/InputActionState.cs | 2 +- .../Editor/Analytics/InputBuildAnalytic.cs | 2 +- .../InputSystem/InputSettings.cs | 6 +++--- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Actions.cs b/Assets/Tests/InputSystem/CoreTests_Actions.cs index fa24a4d915..2d454b40f3 100644 --- a/Assets/Tests/InputSystem/CoreTests_Actions.cs +++ b/Assets/Tests/InputSystem/CoreTests_Actions.cs @@ -211,7 +211,7 @@ public void Actions_WhenShortcutsDisabled_AllConflictingActionsTrigger() [TestCase(false)] public void Actions_WhenShortcutsEnabled_CanConsumeInput(bool legacyComposites) { - InputSystem.settings.shortcutKeysConsumeInput = true; + //InputSystem.settings.shortcutKeysConsumeInput = true; var keyboard = InputSystem.AddDevice(); @@ -303,7 +303,7 @@ public void Actions_WhenShortcutsEnabled_CanConsumeInput(bool legacyComposites) [Category("Actions")] public void Actions_ShortcutSupportDisabledByDefault() { - Assert.That(InputSystem.settings.shortcutKeysConsumeInput, Is.False); + //Assert.That(InputSystem.settings.shortcutKeysConsumeInput, Is.False); var keyboard = InputSystem.AddDevice(); @@ -391,7 +391,7 @@ public void Actions_CanBindMultipleShortcutSequencesBasedOnSameModifiers() [TestCase("leftShift", "leftAlt", "space", false)] public void Actions_WhenShortcutsEnabled_PressingShortcutSequenceInWrongOrder_DoesNotTriggerShortcut(string modifier1, string modifier2, string binding, bool legacyComposites) { - InputSystem.settings.shortcutKeysConsumeInput = true; + //InputSystem.settings.shortcutKeysConsumeInput = true; var keyboard = InputSystem.AddDevice(); @@ -476,7 +476,7 @@ public void Actions_WhenShortcutsDisabled_PressingShortcutSequenceInWrongOrder_D public void Actions_WhenShortcutsAreEnabled_PressingShortcutSequenceInWrongOrder_DoesNotTriggerShortcut_ExceptIfOverridden(string modifier1, string modifier2, string binding, bool legacyComposites, bool overrideModifiersNeedToBePressedFirst) { - InputSystem.settings.shortcutKeysConsumeInput = true; + //InputSystem.settings.shortcutKeysConsumeInput = true; var keyboard = InputSystem.AddDevice(); @@ -514,7 +514,7 @@ public void Actions_WhenShortcutsAreEnabled_PressingShortcutSequenceInWrongOrder [Category("Actions")] public void Actions_WhenShortcutsAreEnabled_CanHaveShortcutsWithButtonsUsingInitialStateChecks() { - InputSystem.settings.shortcutKeysConsumeInput = true; + //InputSystem.settings.shortcutKeysConsumeInput = true; var keyboard = InputSystem.AddDevice(); @@ -1684,7 +1684,7 @@ public void Actions_CanDisableAndEnableOtherAction_FromCallback() public void Actions_CanDisableAndEnable_FromCallbackWhileOtherCompositeBindingIsProgress() { // Enables "Modifier must be pressed first" behavior on all Composite Bindings - InputSystem.settings.shortcutKeysConsumeInput = true; + //InputSystem.settings.shortcutKeysConsumeInput = true; var keyboard = InputSystem.AddDevice(); var map = new InputActionMap("map"); @@ -10521,7 +10521,7 @@ public void Actions_CompositesReportControlThatTriggeredTheCompositeInCallback() public void Actions_CompositesInDifferentMapsTiedToSameControlsWork() { // This test relies on the same single input getting picked up by two different composites. - InputSystem.settings.shortcutKeysConsumeInput = false; + //InputSystem.settings.shortcutKeysConsumeInput = false; var keyboard = InputSystem.AddDevice(); var gamepad = InputSystem.AddDevice(); @@ -12502,7 +12502,7 @@ public void Actions_WithMultipleCompositeBindings_WithoutEvaluateMagnitude_Works [TestCase(false)] public void Actions_ImprovedShortcutSupport_ConsumesWASD(bool shortcutsEnabled) { - InputSystem.settings.shortcutKeysConsumeInput = shortcutsEnabled; + //InputSystem.settings.shortcutKeysConsumeInput = shortcutsEnabled; var keyboard = InputSystem.AddDevice(); diff --git a/Assets/Tests/InputSystem/CoreTests_Analytics.cs b/Assets/Tests/InputSystem/CoreTests_Analytics.cs index 7b75e0669d..7ba5dca4c6 100644 --- a/Assets/Tests/InputSystem/CoreTests_Analytics.cs +++ b/Assets/Tests/InputSystem/CoreTests_Analytics.cs @@ -450,7 +450,7 @@ public void Analytics_ShouldReportBuildAnalytics_WhenNotHavingSettingsAsset() Assert.That(data.max_queued_events_per_update, Is.EqualTo(defaultSettings.maxQueuedEventsPerUpdate)); Assert.That(data.supported_devices, Is.EqualTo(defaultSettings.supportedDevices)); Assert.That(data.disable_redundant_events_merging, Is.EqualTo(defaultSettings.disableRedundantEventsMerging)); - Assert.That(data.shortcut_keys_consume_input, Is.EqualTo(defaultSettings.shortcutKeysConsumeInput)); + //Assert.That(data.shortcut_keys_consume_input, Is.EqualTo(defaultSettings.shortcutKeysConsumeInput)); Assert.That(data.feature_optimized_controls_enabled, Is.EqualTo(defaultSettings.IsFeatureEnabled(InputFeatureNames.kUseOptimizedControls))); Assert.That(data.feature_read_value_caching_enabled, Is.EqualTo(defaultSettings.IsFeatureEnabled(InputFeatureNames.kUseReadValueCaching))); @@ -498,7 +498,7 @@ public void Analytics_ShouldReportBuildAnalytics_WhenHavingSettingsAssetWithCust customSettings.maxQueuedEventsPerUpdate = 12; customSettings.supportedDevices = Array.Empty(); customSettings.disableRedundantEventsMerging = true; - customSettings.shortcutKeysConsumeInput = true; + //customSettings.shortcutKeysConsumeInput = true; customSettings.SetInternalFeatureFlag(InputFeatureNames.kUseOptimizedControls, true); customSettings.SetInternalFeatureFlag(InputFeatureNames.kParanoidReadValueCachingChecks, true); @@ -544,7 +544,7 @@ public void Analytics_ShouldReportBuildAnalytics_WhenHavingSettingsAssetWithCust Assert.That(data.max_queued_events_per_update, Is.EqualTo(customSettings.maxQueuedEventsPerUpdate)); Assert.That(data.supported_devices, Is.EqualTo(customSettings.supportedDevices)); Assert.That(data.disable_redundant_events_merging, Is.EqualTo(customSettings.disableRedundantEventsMerging)); - Assert.That(data.shortcut_keys_consume_input, Is.EqualTo(customSettings.shortcutKeysConsumeInput)); + //Assert.That(data.shortcut_keys_consume_input, Is.EqualTo(customSettings.shortcutKeysConsumeInput)); Assert.That(data.feature_optimized_controls_enabled, Is.True); Assert.That(data.feature_read_value_caching_enabled, Is.True); diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithOneModifier.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithOneModifier.cs index 75d6f333a3..06621ef5ad 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithOneModifier.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithOneModifier.cs @@ -196,7 +196,7 @@ protected override void FinishSetup(ref InputBindingCompositeContext context) #pragma warning restore CS0618 modifiersOrder = ModifiersOrder.Unordered; else - modifiersOrder = InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered : ModifiersOrder.Unordered; + modifiersOrder = /*InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered :*/ ModifiersOrder.Unordered; } } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithTwoModifiers.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithTwoModifiers.cs index fcce470071..bcc78c448a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithTwoModifiers.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/ButtonWithTwoModifiers.cs @@ -211,7 +211,7 @@ protected override void FinishSetup(ref InputBindingCompositeContext context) #pragma warning restore CS0618 modifiersOrder = ModifiersOrder.Unordered; else - modifiersOrder = InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered : ModifiersOrder.Unordered; + modifiersOrder = /*InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered :*/ ModifiersOrder.Unordered; } } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/OneModifierComposite.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/OneModifierComposite.cs index 15c720145b..2c51fcf76a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/OneModifierComposite.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/OneModifierComposite.cs @@ -214,7 +214,7 @@ protected override void FinishSetup(ref InputBindingCompositeContext context) #pragma warning restore CS0618 modifiersOrder = ModifiersOrder.Unordered; else - modifiersOrder = InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered : ModifiersOrder.Unordered; + modifiersOrder = /* InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered :*/ ModifiersOrder.Unordered; } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/TwoModifiersComposite.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/TwoModifiersComposite.cs index 8044a628e4..8750cc41b2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/TwoModifiersComposite.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/TwoModifiersComposite.cs @@ -224,7 +224,7 @@ protected override void FinishSetup(ref InputBindingCompositeContext context) #pragma warning restore CS0618 modifiersOrder = ModifiersOrder.Unordered; else - modifiersOrder = InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered : ModifiersOrder.Unordered; + modifiersOrder = /*InputSystem.settings.shortcutKeysConsumeInput ? ModifiersOrder.Ordered :*/ ModifiersOrder.Unordered; } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs index dd4cb7d6de..b181ff14cc 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs @@ -148,7 +148,7 @@ private void ComputeControlGroupingIfNecessary() // If shortcut support is disabled, we simply put put all bindings at complexity=1 and // in their own group. - var disableControlGrouping = !InputSystem.settings.shortcutKeysConsumeInput; + var disableControlGrouping = true; // !InputSystem.settings.shortcutKeysConsumeInput; var currentGroup = 1u; for (var i = 0; i < totalControlCount; ++i) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Analytics/InputBuildAnalytic.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Analytics/InputBuildAnalytic.cs index 61256e5a78..ee931e2eef 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Analytics/InputBuildAnalytic.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Analytics/InputBuildAnalytic.cs @@ -188,7 +188,7 @@ public InputBuildAnalyticData(BuildReport report, InputSettings settings, InputS max_queued_events_per_update = settings.maxQueuedEventsPerUpdate; supported_devices = settings.supportedDevices.ToArray(); disable_redundant_events_merging = settings.disableRedundantEventsMerging; - shortcut_keys_consume_input = settings.shortcutKeysConsumeInput; + shortcut_keys_consume_input = false; // settings.shortcutKeysConsumeInput; feature_optimized_controls_enabled = settings.IsFeatureEnabled(InputFeatureNames.kUseOptimizedControls); feature_read_value_caching_enabled = settings.IsFeatureEnabled(InputFeatureNames.kUseReadValueCaching); diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs b/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs index 1fa27fc29e..16a60318e6 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs @@ -691,7 +691,7 @@ public bool disableRedundantEventsMerging /// These conflicts may occur even between actions which belong to different Action Maps e.g. if using an UIInputModule with the Arrow Keys bound to the Navigate Action in the UI Action Map, this would interfere with other Action Maps using those keys. /// However conflicts would not occur between actions which belong to different Action Assets. /// - public bool shortcutKeysConsumeInput + /*public bool shortcutKeysConsumeInput { get => m_ShortcutKeysConsumeInputs; set @@ -702,7 +702,7 @@ public bool shortcutKeysConsumeInput m_ShortcutKeysConsumeInputs = value; OnChange(); } - } + }*/ /// /// Enable or disable an internal feature by its name. @@ -1054,7 +1054,7 @@ internal static bool AreEqual(InputSettings a, InputSettings b) a.maxQueuedEventsPerUpdate == b.maxQueuedEventsPerUpdate && CompareSets(a.supportedDevices, b.supportedDevices) && a.disableRedundantEventsMerging == b.disableRedundantEventsMerging && - a.shortcutKeysConsumeInput == b.shortcutKeysConsumeInput && + //a.shortcutKeysConsumeInput == b.shortcutKeysConsumeInput && CompareFeatureFlag(a, b, InputFeatureNames.kUseOptimizedControls) && CompareFeatureFlag(a, b, InputFeatureNames.kUseReadValueCaching) && From 1eef02c17b53708c5b210a11163a6836b4b6bed3 Mon Sep 17 00:00:00 2001 From: Darren Kelly Date: Thu, 19 Mar 2026 12:46:12 +0100 Subject: [PATCH 02/22] Add progress with solution for priority order for conflicting shortcuts. --- .../ProjectWideActionsExample.cs | 38 +++++++++++ .../InputSystem/Actions/InputActionState.cs | 64 ++++++++++++------- 2 files changed, 78 insertions(+), 24 deletions(-) diff --git a/Assets/Samples/ProjectWideActions/ProjectWideActionsExample.cs b/Assets/Samples/ProjectWideActions/ProjectWideActionsExample.cs index 8b066102dc..32c49901e2 100644 --- a/Assets/Samples/ProjectWideActions/ProjectWideActionsExample.cs +++ b/Assets/Samples/ProjectWideActions/ProjectWideActionsExample.cs @@ -13,6 +13,8 @@ public class ProjectWideActionsExample : MonoBehaviour InputAction previous; InputAction sprint; InputAction crouch; + InputAction b; + InputAction shiftB; // Start is called before the first frame update void Start() @@ -29,6 +31,8 @@ void Start() previous = InputSystem.actions.FindAction("Player/Previous"); sprint = InputSystem.actions.FindAction("Player/Sprint"); crouch = InputSystem.actions.FindAction("Player/Crouch"); + b = InputSystem.actions.FindAction("Player/B"); + shiftB = InputSystem.actions.FindAction("Player/Shift B"); } else { @@ -41,6 +45,18 @@ void Start() attack.performed += OnAttack; attack.canceled += OnCancel; } + + if (b != null) + { + b.performed += OnB; + b.canceled += OnCancel; + } + + if (shiftB != null) + { + shiftB.performed += OnShiftB; + shiftB.canceled += OnCancel; + } } private void OnAttack(InputAction.CallbackContext ctx) @@ -53,6 +69,18 @@ private void OnCancel(InputAction.CallbackContext ctx) cube.GetComponent().material.color = Color.green; } + private void OnB(InputAction.CallbackContext ctx) + { + Debug.Log("B WAS PRESSED"); + cube.GetComponent().material.color = Color.yellow; + } + + private void OnShiftB(InputAction.CallbackContext ctx) + { + Debug.Log("SHIFT + B WAS PRESSED"); + cube.GetComponent().material.color = Color.blue; + } + void OnDestroy() { if (attack != null) @@ -60,6 +88,16 @@ void OnDestroy() attack.performed -= OnAttack; attack.canceled -= OnCancel; } + if (b != null) + { + b.performed -= OnB; + b.canceled -= OnCancel; + } + if (shiftB != null) + { + shiftB.performed -= OnShiftB; + shiftB.canceled -= OnCancel; + } } // Update is called once per frame diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs index b181ff14cc..ad1f3869a7 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs @@ -148,52 +148,68 @@ private void ComputeControlGroupingIfNecessary() // If shortcut support is disabled, we simply put put all bindings at complexity=1 and // in their own group. - var disableControlGrouping = true; // !InputSystem.settings.shortcutKeysConsumeInput; + //var disableControlGrouping = true; // !InputSystem.settings.shortcutKeysConsumeInput; var currentGroup = 1u; + for (var i = 0; i < totalControlCount; ++i) { var control = controls[i]; - var bindingIndex = controlIndexToBindingIndex[i]; - ref var binding = ref bindingStates[bindingIndex]; + + int bindingIndex = controlIndexToBindingIndex[i]; + ref BindingState binding = ref bindingStates[bindingIndex]; ////REVIEW: take processors and interactions into account?? // Compute complexity. var complexity = 1; - if (binding.isPartOfComposite && !disableControlGrouping) - { - var compositeBindingIndex = binding.compositeOrCompositeBindingIndex; + Debug.Log("Name " + control.name + " Action count " + memory.actionCount + " memory action state " + memory.actionStates[0].controlIndex); + + /*if (binding.isPartOfComposite && !disableControlGrouping) + {*/ + //int compositeBindingIndex = binding.compositeOrCompositeBindingIndex; - for (var n = compositeBindingIndex + 1; n < totalBindingCount; ++n) + /*for (var n = compositeBindingIndex + 1; n < totalBindingCount; ++n) + { + ref BindingState partBinding = ref bindingStates[n]; + if (partBinding.actionIndex == 10) { - ref var partBinding = ref bindingStates[n]; - if (!partBinding.isPartOfComposite || partBinding.compositeOrCompositeBindingIndex != compositeBindingIndex) - break; - ++complexity; + complexity = 2; + Debug.Log("Complexity was set to 2"); + break; } + /*if (!partBinding.isPartOfComposite || partBinding.compositeOrCompositeBindingIndex != compositeBindingIndex) + break;#1# + //++complexity; + }*/ + if (binding.actionIndex == 10) + { + complexity = 2; + Debug.Log("Complexity was set to 2"); } + + /*}*/ controlGroupingAndComplexity[i * 2 + 1] = (ushort)complexity; // Compute grouping. If already set, skip. if (controlGroupingAndComplexity[i * 2] == 0) { - if (!disableControlGrouping) + //if (!disableControlGrouping) + //{ + for (var n = 0; n < totalControlCount; ++n) { - for (var n = 0; n < totalControlCount; ++n) - { - // NOTE: We could compute group numbers based on device index + control offsets - // and thus make them work globally in a stable way. But we'd need a mechanism - // to then determine ordering of actions globally such that it is clear which - // action gets a first shot at an input. + // NOTE: We could compute group numbers based on device index + control offsets + // and thus make them work globally in a stable way. But we'd need a mechanism + // to then determine ordering of actions globally such that it is clear which + // action gets a first shot at an input. - var otherControl = controls[n]; - if (control != otherControl) - continue; + var otherControl = controls[n]; + if (control != otherControl) + continue; - controlGroupingAndComplexity[n * 2] = (ushort)currentGroup; - } + controlGroupingAndComplexity[n * 2] = (ushort)currentGroup; } + //} controlGroupingAndComplexity[i * 2] = (ushort)currentGroup; @@ -556,7 +572,7 @@ private void RestoreActionStatesAfterReResolvingBindings(UnmanagedMemory oldStat // Restore action states. for (var actionIndex = 0; actionIndex < totalActionCount; ++actionIndex) { - ref var oldActionState = ref oldState.actionStates[actionIndex]; + ref TriggerState oldActionState = ref oldState.actionStates[actionIndex]; ref var newActionState = ref actionStates[actionIndex]; newActionState.lastCanceledInUpdate = oldActionState.lastCanceledInUpdate; From d4c1f3f4a2c736500cb6274400a39709afc36ee1 Mon Sep 17 00:00:00 2001 From: Darren Kelly Date: Fri, 20 Mar 2026 17:30:39 +0100 Subject: [PATCH 03/22] Add progress with investigation to switch to priority calculation. --- .../InputSystem/Actions/InputActionState.cs | 25 ++++++++++--------- .../InputSystem/Actions/InputBinding.cs | 8 ++++++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs index ad1f3869a7..595466891c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs @@ -112,7 +112,7 @@ internal unsafe class InputActionState : IInputStateChangeMonitor, ICloneable, I public BindingState* bindingStates => memory.bindingStates; public InteractionState* interactionStates => memory.interactionStates; public int* controlIndexToBindingIndex => memory.controlIndexToBindingIndex; - public ushort* controlGroupingAndComplexity => memory.controlGroupingAndComplexity; + public ushort* controlGroupingAndPriority => memory.controlGroupingAndComplexity; public float* controlMagnitudes => memory.controlMagnitudes; public uint* enabledControls => (uint*)memory.enabledControls; @@ -162,8 +162,9 @@ private void ComputeControlGroupingIfNecessary() ////REVIEW: take processors and interactions into account?? // Compute complexity. - var complexity = 1; - Debug.Log("Name " + control.name + " Action count " + memory.actionCount + " memory action state " + memory.actionStates[0].controlIndex); + InputBinding inputBinding = GetBinding(bindingIndex); + var priority = inputBinding.Priority; + Debug.Log("Action Name " + inputBinding.action + " Action count " + memory.actionCount + " memory action state " + memory.actionStates[0].controlIndex); /*if (binding.isPartOfComposite && !disableControlGrouping) {*/ @@ -184,15 +185,15 @@ private void ComputeControlGroupingIfNecessary() }*/ if (binding.actionIndex == 10) { - complexity = 2; + priority = 2; Debug.Log("Complexity was set to 2"); } /*}*/ - controlGroupingAndComplexity[i * 2 + 1] = (ushort)complexity; + controlGroupingAndPriority[i * 2 + 1] = (ushort)priority; // Compute grouping. If already set, skip. - if (controlGroupingAndComplexity[i * 2] == 0) + if (controlGroupingAndPriority[i * 2] == 0) { //if (!disableControlGrouping) //{ @@ -207,11 +208,11 @@ private void ComputeControlGroupingIfNecessary() if (control != otherControl) continue; - controlGroupingAndComplexity[n * 2] = (ushort)currentGroup; + controlGroupingAndPriority[n * 2] = (ushort)currentGroup; } //} - controlGroupingAndComplexity[i * 2] = (ushort)currentGroup; + controlGroupingAndPriority[i * 2] = (ushort)currentGroup; ++currentGroup; } @@ -1182,7 +1183,7 @@ private void EnableControls(int mapIndex, int controlStartIndex, int numControls var bindingStatePtr = &bindingStates[bindingIndex]; if (bindingStatePtr->wantsInitialStateCheck) SetInitialStateCheckPending(bindingStatePtr, true); - manager.AddStateChangeMonitor(controls[controlIndex], this, mapControlAndBindingIndex, controlGroupingAndComplexity[controlIndex * 2]); + manager.AddStateChangeMonitor(controls[controlIndex], this, mapControlAndBindingIndex, controlGroupingAndPriority[controlIndex * 2]); SetControlEnabled(controlIndex, true); } @@ -1404,7 +1405,7 @@ private long ToCombinedMapAndControlAndBindingIndex(int mapIndex, int controlInd { // We have limits on the numbers of maps, controls, and bindings we allow in any single // action state (see TriggerState.kMaxNumXXX). - var complexity = controlGroupingAndComplexity[controlIndex * 2 + 1]; + var complexity = controlGroupingAndPriority[controlIndex * 2 + 1]; var result = (long)controlIndex; result |= (long)bindingIndex << 24; result |= (long)mapIndex << 40; @@ -2494,7 +2495,7 @@ private void ChangePhaseOfActionInternal(int actionIndex, TriggerState* actionSt // When we perform an action, we mark the event handled such that FireStateChangeNotifications() // can then reset state monitors in the same group. // NOTE: We don't consume for controls at binding complexity 1. Those we fire in unison. - if (controlGroupingAndComplexity[trigger.controlIndex * 2 + 1] > 1 && + if (controlGroupingAndPriority[trigger.controlIndex * 2 + 1] > 1 && // we can end up switching to performed state from an interaction with a timeout, at which point // the original event will probably have been removed from memory, so make sure to check // we still have one @@ -4188,7 +4189,7 @@ public struct UnmanagedMemory : IDisposable ////REVIEW: make this an array of shorts rather than ints? public int* controlIndexToBindingIndex; - // Two shorts per control. First one is group number. Second one is complexity count. + // Two shorts per control. First one is group number. Second one is priority. public ushort* controlGroupingAndComplexity; public bool controlGroupingInitialized; diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs index 45b4c3806c..8db37626b2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs @@ -106,6 +106,12 @@ public Guid id set => m_Id = value.ToString(); } + public int Priority + { + get => m_Priority; + set => m_Priority = value; + } + /// /// Control path being bound to. /// @@ -396,6 +402,7 @@ public InputBinding(string path, string action = null, string groups = null, str m_OverridePath = default; m_OverrideInteractions = default; m_OverrideProcessors = default; + m_Priority = default; } public string GetNameOfComposite() @@ -429,6 +436,7 @@ public static InputBinding MaskByGroups(params string[] groups) [SerializeField] private string m_Name; [SerializeField] internal string m_Id; + [SerializeField] internal int m_Priority; [Tooltip("Path of the control to bind to. Matched at runtime to controls from InputDevices present at the time.\n\nCan either be " + "graphically from the control picker dropdown UI or edited manually in text mode by clicking the 'T' button. Internally, both " + "methods result in control path strings that look like, for example, \"/buttonSouth\".")] From 675afcec1cef3ee00e9617121f4e3df32f2aba09 Mon Sep 17 00:00:00 2001 From: Darren Kelly Date: Mon, 23 Mar 2026 13:22:38 +0100 Subject: [PATCH 04/22] Implement binding priority feature in Input System, including UI support and clamping logic. Add tooltip for priority field and update related classes to handle priority in bindings. --- .../InputSystem/Actions/InputActionMap.cs | 9 +++++++ .../InputSystem/Actions/InputBinding.cs | 11 +++++++++ .../UITKAssetEditor/Commands/Commands.cs | 13 ++++++++++ .../InputActionsEditorConstants.cs | 5 ++++ .../UITKAssetEditor/SerializedInputBinding.cs | 4 ++++ .../Views/BindingPropertiesView.cs | 24 +++++++++++++++++++ 6 files changed, 66 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs index a07f286f7a..59eae5a381 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs @@ -1521,9 +1521,16 @@ internal struct BindingJson public string action; public bool isComposite; public bool isPartOfComposite; + public int priority; public InputBinding ToBinding() { + var clampedPriority = priority; + if (clampedPriority < 0) + clampedPriority = 0; + else if (clampedPriority > 65535) + clampedPriority = 65535; + return new InputBinding { name = string.IsNullOrEmpty(name) ? null : name, @@ -1535,6 +1542,7 @@ public InputBinding ToBinding() groups = string.IsNullOrEmpty(groups) ? null : groups, isComposite = isComposite, isPartOfComposite = isPartOfComposite, + m_Priority = clampedPriority, }; } @@ -1551,6 +1559,7 @@ public static BindingJson FromBinding(ref InputBinding binding) groups = binding.groups, isComposite = binding.isComposite, isPartOfComposite = binding.isPartOfComposite, + priority = binding.m_Priority, }; } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs index 8db37626b2..0a12bc42e6 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputBinding.cs @@ -106,6 +106,17 @@ public Guid id set => m_Id = value.ToString(); } + /// + /// Priority of this binding when multiple bindings resolve to the same control. + /// + /// Effective range at runtime is 0–65535; the value is combined with control grouping data as an unsigned 16-bit integer. + /// + /// This influences how the input system handles overlapping bindings on a shared control—for example whether + /// a performed action can mark the underlying input event as handled, which affects further processing for + /// other bindings in the same group. Values 0–1 follow one path; values greater than 1 follow another when + /// the input system resolves overlapping bindings on the same control. Values outside the 0–65535 range are + /// truncated when stored in the internal representation. + /// public int Priority { get => m_Priority; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs index 3cfcf2df44..048de62d67 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using UnityEditor; +using UnityEngine; using UnityEngine.InputSystem.Editor.Lists; using UnityEngine.InputSystem.Utilities; @@ -534,6 +535,18 @@ public static Command SetCompositeBindingPartName(SerializedInputBinding binding }; } + public static Command ChangeBindingPriority(SerializedInputBinding binding, int priority) + { + return (in InputActionsEditorState state) => + { + var priorityProperty = binding.wrappedProperty.FindPropertyRelative(nameof(InputBinding.m_Priority)); + priorityProperty.intValue = Mathf.Clamp(priority, 0, 65535); + state.serializedObject.ApplyModifiedProperties(); + state.m_Analytics?.RegisterBindingEdit(); + return state; + }; + } + public static Command ChangeActionType(SerializedInputAction inputAction, InputActionType newValue) { return (in InputActionsEditorState state) => diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorConstants.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorConstants.cs index c0c2c07c3b..62a9a01176 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorConstants.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorConstants.cs @@ -34,6 +34,11 @@ internal static class InputActionsEditorConstants + "immediately trigger if any of its bound controls are currently in a non-default state. " + "This check happens implicitly for Value actions but can be explicitly enabled for Button and Pass-Through actions."; + public const string BindingPriorityTooltip = + "Priority when several bindings share the same control. Effective range is 0–65535 at runtime (unsigned 16-bit). " + + "It affects how overlapping bindings are processed—for example whether a performed action can mark the input " + + "event as handled. Values 0–1 behave differently from values greater than 1 in that regard."; + public struct CommandEvents { public const string Rename = "Rename"; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs index 16a81f6177..0c3e879526 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs @@ -30,6 +30,7 @@ public SerializedInputBinding(SerializedProperty serializedProperty) ? bindingGroups.Split(InputBinding.kSeparatorString, StringSplitOptions.RemoveEmptyEntries) : Array.Empty(); flags = (InputBinding.Flags)serializedProperty.FindPropertyRelative(nameof(InputBinding.m_Flags)).intValue; + priority = serializedProperty.FindPropertyRelative(nameof(InputBinding.m_Priority)).intValue; indexOfBinding = serializedProperty.GetIndexOfArrayElement(); isComposite = (flags & InputBinding.Flags.Composite) == InputBinding.Flags.Composite; isPartOfComposite = (flags & InputBinding.Flags.PartOfComposite) == InputBinding.Flags.PartOfComposite; @@ -48,6 +49,7 @@ public SerializedInputBinding(SerializedProperty serializedProperty) public string propertyPath { get; } public string[] controlSchemes { get; } public InputBinding.Flags flags { get; } + public int priority { get; } /// /// The index of this binding in the array that it is stored in. @@ -85,6 +87,7 @@ public bool Equals(SerializedInputBinding other) && processors == other.processors && action == other.action && flags == other.flags + && priority == other.priority && indexOfBinding == other.indexOfBinding && isComposite == other.isComposite && isPartOfComposite == other.isPartOfComposite @@ -107,6 +110,7 @@ public override int GetHashCode() hashCode.Add(processors); hashCode.Add(action); hashCode.Add((int)flags); + hashCode.Add(priority); hashCode.Add(indexOfBinding); hashCode.Add(isComposite); hashCode.Add(isPartOfComposite); diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs index 1ef2eff60e..02ab2bf6f6 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs @@ -1,6 +1,7 @@ #if UNITY_EDITOR using System.Linq; using UnityEditor; +using UnityEditor.UIElements; using UnityEngine.UIElements; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.Utilities; @@ -13,6 +14,7 @@ internal class BindingPropertiesView : ViewBase private readonly Foldout m_ParentFoldout; private CompositeBindingPropertiesView m_CompositeBindingPropertiesView; private CompositePartBindingPropertiesView m_CompositePartBindingPropertiesView; + private const int k_PriorityLabelWidth = 90; public BindingPropertiesView(VisualElement root, Foldout foldout, StateContainer stateContainer) : base(root, stateContainer) @@ -49,10 +51,12 @@ public override void RedrawUI(ViewState viewState) { m_ParentFoldout.text = "Composite"; m_CompositeBindingPropertiesView = CreateChildView(new CompositeBindingPropertiesView(rootElement, stateContainer)); + DrawPriorityField(binding.Value); } else if (binding.Value.isPartOfComposite) { m_CompositePartBindingPropertiesView = CreateChildView(new CompositePartBindingPropertiesView(rootElement, stateContainer)); + DrawPriorityField(binding.Value); DrawMatchingControlPaths(viewState); DrawControlSchemeToggles(viewState, binding.Value); } @@ -68,11 +72,31 @@ public override void RedrawUI(ViewState viewState) var controlPathContainer = new IMGUIContainer(controlPathEditor.OnGUI); rootElement.Add(controlPathContainer); + DrawPriorityField(binding.Value); DrawMatchingControlPaths(viewState); DrawControlSchemeToggles(viewState, binding.Value); } } + private void DrawPriorityField(SerializedInputBinding binding) + { + var priorityField = new IntegerField("Priority") + { + tooltip = InputActionsEditorConstants.BindingPriorityTooltip + }; + var priorityLabel = priorityField.Q