Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
28640eb
Comment out the shortcutKeysConsume input setting.
Darren-Kelly-Unity Mar 17, 2026
1eef02c
Add progress with solution for priority order for conflicting shortcuts.
Darren-Kelly-Unity Mar 19, 2026
d4c1f3f
Add progress with investigation to switch to priority calculation.
Darren-Kelly-Unity Mar 20, 2026
675afce
Implement binding priority feature in Input System, including UI supp…
Darren-Kelly-Unity Mar 23, 2026
3ea57de
Add priority to Actions instead of bindings.
Darren-Kelly-Unity Mar 23, 2026
fb68220
Add input actions asset.
Darren-Kelly-Unity Mar 23, 2026
423c64e
Revert "Comment out the shortcutKeysConsume input setting."
Darren-Kelly-Unity Mar 23, 2026
cc7adfe
Add better setup.
Darren-Kelly-Unity Mar 23, 2026
9cb60d1
priority is currently zero by default
K-Tone Mar 23, 2026
fa1afac
simplify just a wee bit
K-Tone Mar 24, 2026
51df2ef
Add progress with setting up tests.
Darren-Kelly-Unity Mar 25, 2026
ec0683b
Add current progress with test creation.
Darren-Kelly-Unity Mar 26, 2026
c97af36
Add the first working test!!
Darren-Kelly-Unity Mar 26, 2026
762e1ed
Add second working test.
Darren-Kelly-Unity Mar 26, 2026
7237d8e
Add new test with repeated binding press to be sure it doesn't trigge…
Darren-Kelly-Unity Mar 27, 2026
eddb699
Add test cases for all that I could think of.
Darren-Kelly-Unity Mar 27, 2026
b7d5a5d
Fix broken test due to shortcutKeysConsumeInput setting.
Darren-Kelly-Unity Mar 30, 2026
29aaa21
Add progress with refactoring tests to be more streamlined / re-usabl…
Darren-Kelly-Unity Apr 1, 2026
86d55b1
Add progress with converting / refactoring tests to use TestCaseSource.
Darren-Kelly-Unity Apr 2, 2026
7836085
Fix two broken tests due to key ordering of presses.
Darren-Kelly-Unity Apr 2, 2026
ca4c908
Add new test case that passes even though I wouldn't expect it to.
Darren-Kelly-Unity Apr 2, 2026
707433a
Update tests and remove redundant test.
Darren-Kelly-Unity Apr 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
615 changes: 615 additions & 0 deletions Assets/InputSystem_Actions.inputactions

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions Assets/InputSystem_Actions.inputactions.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions Assets/Samples/ProjectWideActions/ProjectWideActionsExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
{
Expand All @@ -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)
Expand All @@ -53,13 +69,35 @@ private void OnCancel(InputAction.CallbackContext ctx)
cube.GetComponent<Renderer>().material.color = Color.green;
}

private void OnB(InputAction.CallbackContext ctx)
{
Debug.Log("B WAS PRESSED");
cube.GetComponent<Renderer>().material.color = Color.yellow;
}

private void OnShiftB(InputAction.CallbackContext ctx)
{
Debug.Log("SHIFT + B WAS PRESSED");
cube.GetComponent<Renderer>().material.color = Color.blue;
}

void OnDestroy()
{
if (attack != null)
{
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
Expand All @@ -71,6 +109,16 @@ void Update()
var moveVal = move.ReadValue<Vector2>() * 10.0f * Time.deltaTime;
cube.transform.Translate(new Vector3(moveVal.x, moveVal.y, 0));
}

if (shiftB.IsPressed())
{
Debug.Log("SHIFT + B WAS PRESSED");
}

if (b.IsPressed())
{
Debug.Log("B WAS PRESSED");
}
Comment on lines +113 to +121
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high
These calls to IsPressed() lack null checks. Since shiftB and b are resolved via FindAction in Start and are guarded by null checks elsewhere (lines 49 and 55), they could potentially be null. Accessing them here without a check will cause a NullReferenceException every frame if the actions are missing from the asset.

Additionally, polling IsPressed() and logging here spams the console every frame while the keys are held. This is likely redundant since the same messages are already logged once per press via the event callbacks (OnB, OnShiftB).

🤖 Helpful? 👍/👎

}
} // class ProjectWideActionsExample
} // namespace UnityEngine.InputSystem.Samples.ProjectWideActions
85 changes: 35 additions & 50 deletions Assets/Tests/InputSystem/CoreTests_Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1594,14 +1594,14 @@ public void Actions_ActiveBindingsHaveCorrectBindingIndicesAfterBindingResolutio
// with the control (i.e. mouse.leftButton) or with action callbacks
// could all appear correct because those don't actually use bindingIndex.
// This issue originally manifested itself as an assert in another place in the code.
InputSystem.RegisterProcessor<ConstantFloatTestProcessor>();
InputSystem.RegisterProcessor<CoreTests.ConstantFloatTestProcessor>();

// This test is sensitive to binding order.
// It's important that the active binding is not in the first
// position of the action (i.e. not at the default index).
var map = new InputActionMap("map");
var action = map.AddAction("action1", binding: "<Gamepad>/buttonSouth");
action.AddBinding("<Mouse>/leftButton").WithProcessor<ConstantFloatTestProcessor>(); // binding in 2nd position.
action.AddBinding("<Mouse>/leftButton").WithProcessor<CoreTests.ConstantFloatTestProcessor>(); // binding in 2nd position.
map.Enable();

var mouse = InputSystem.AddDevice<Mouse>();
Expand Down Expand Up @@ -4279,7 +4279,7 @@ public void Actions_WithMultipleBoundControls_CanHandleInteractionsThatTriggerOn
var keyboard = InputSystem.AddDevice<Keyboard>();
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterInteraction<ReleaseOnlyTestInteraction>();
InputSystem.RegisterInteraction<CoreTests.ReleaseOnlyTestInteraction>();

var action = new InputAction(interactions: "releaseOnlyTest");

Expand Down Expand Up @@ -5526,28 +5526,13 @@ public ModificationCases() {}

private static readonly Modification[] ModificationAppliesToSingleActionMap =
{
Modification.AddBinding,
Modification.RemoveBinding,
Modification.ModifyBinding,
Modification.ApplyBindingOverride,
Modification.AddAction,
Modification.RemoveAction,
Modification.ChangeBindingMask,
Modification.AddDevice,
Modification.RemoveDevice,
Modification.AddDeviceGlobally,
Modification.RemoveDeviceGlobally,
CoreTests.Modification.AddBinding, CoreTests.Modification.RemoveBinding, CoreTests.Modification.ModifyBinding, CoreTests.Modification.ApplyBindingOverride, CoreTests.Modification.AddAction, CoreTests.Modification.RemoveAction, CoreTests.Modification.ChangeBindingMask, CoreTests.Modification.AddDevice, CoreTests.Modification.RemoveDevice, CoreTests.Modification.AddDeviceGlobally, CoreTests.Modification.RemoveDeviceGlobally,
// Excludes: AddMap, RemoveMap
};

private static readonly Modification[] ModificationAppliesToSingletonAction =
{
Modification.AddBinding,
Modification.RemoveBinding,
Modification.ModifyBinding,
Modification.ApplyBindingOverride,
Modification.AddDeviceGlobally,
Modification.RemoveDeviceGlobally,
CoreTests.Modification.AddBinding, CoreTests.Modification.RemoveBinding, CoreTests.Modification.ModifyBinding, CoreTests.Modification.ApplyBindingOverride, CoreTests.Modification.AddDeviceGlobally, CoreTests.Modification.RemoveDeviceGlobally,
};

public IEnumerator GetEnumerator()
Expand Down Expand Up @@ -5615,7 +5600,7 @@ private InputActionMap CreateSingletonAction()

[Test]
[Category("Actions")]
[TestCaseSource(typeof(ModificationCases))]
[TestCaseSource(typeof(CoreTests.ModificationCases))]
public void Actions_CanHandleModification(Modification modification, Func<IInputActionCollection2> getActions)
{
// Exclude project-wide actions from this test
Expand Down Expand Up @@ -6177,7 +6162,7 @@ public void Actions_CanAddProcessorsToActions()
{
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterProcessor<ConstantVector2TestProcessor>();
InputSystem.RegisterProcessor<CoreTests.ConstantVector2TestProcessor>();
var action = new InputAction(processors: "ConstantVector2Test");
action.AddBinding("<Gamepad>/leftStick");
action.Enable();
Expand All @@ -6203,7 +6188,7 @@ public void Actions_IncompatibleProcessorIsIgnored()
{
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterProcessor<ConstantVector2TestProcessor>();
InputSystem.RegisterProcessor<CoreTests.ConstantVector2TestProcessor>();
var action = new InputAction(processors: "ConstantVector2Test");
action.AddBinding("<Gamepad>/leftStick/x");
action.Enable();
Expand Down Expand Up @@ -6239,9 +6224,9 @@ public void Actions_CanAddProcessorsToBindings()
{
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterProcessor<ConstantVector2TestProcessor>();
InputSystem.RegisterProcessor<CoreTests.ConstantVector2TestProcessor>();
var action = new InputAction();
action.AddBinding("<Gamepad>/leftStick").WithProcessor<ConstantVector2TestProcessor>();
action.AddBinding("<Gamepad>/leftStick").WithProcessor<CoreTests.ConstantVector2TestProcessor>();
action.Enable();

Vector2? receivedVector = null;
Expand Down Expand Up @@ -6349,13 +6334,13 @@ public override float Process(float value, InputControl control)
[Category("Actions")]
public void Actions_AddingSameProcessorTwice_DoesntImpactUIHideState()
{
InputSystem.RegisterProcessor<ConstantFloat1TestProcessor>();
InputSystem.RegisterProcessor<CoreTests.ConstantFloat1TestProcessor>();
Assert.That(InputSystem.TryGetProcessor("ConstantFloat1Test"), Is.Not.EqualTo(null));

bool hide = InputSystem.manager.processors.ShouldHideInUI("ConstantFloat1Test");
Assert.That(hide, Is.EqualTo(false));

InputSystem.RegisterProcessor<ConstantFloat1TestProcessor>();
InputSystem.RegisterProcessor<CoreTests.ConstantFloat1TestProcessor>();
// Check we haven't caused this to alias with itself and cause it to be hidden in the UI
hide = InputSystem.manager.processors.ShouldHideInUI("ConstantFloat1Test");
Assert.That(hide, Is.EqualTo(false));
Expand All @@ -6369,8 +6354,8 @@ public void Actions_AddingSameNamedProcessorWithDifferentResult_OverridesOrigina
{
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterProcessor<ConstantFloat1TestProcessor>("ConstantFloatTest");
InputSystem.RegisterProcessor<ConstantFloat2TestProcessor>("ConstantFloatTest");
InputSystem.RegisterProcessor<CoreTests.ConstantFloat1TestProcessor>("ConstantFloatTest");
InputSystem.RegisterProcessor<CoreTests.ConstantFloat2TestProcessor>("ConstantFloatTest");

var action = new InputAction(processors: "ConstantFloatTest");
action.AddBinding("<Gamepad>/leftTrigger");
Expand Down Expand Up @@ -6973,7 +6958,7 @@ public void Reset()
[Category("Actions")]
public void Actions_CanRegisterNewInteraction()
{
InputSystem.RegisterInteraction<TestInteraction>();
InputSystem.RegisterInteraction<CoreTests.TestInteraction>();
TestInteraction.s_GotInvoked = false;

var gamepad = InputSystem.AddDevice("Gamepad");
Expand Down Expand Up @@ -9325,7 +9310,7 @@ private class CompositeWithParameters : InputBindingComposite<float>
public bool boolParameter;
public EnumParameter enumParameter;

public static CompositeWithParameters s_Instance;
public static CoreTests.CompositeWithParameters s_Instance;

public CompositeWithParameters()
{
Expand Down Expand Up @@ -9354,7 +9339,7 @@ public override float EvaluateMagnitude(ref InputBindingCompositeContext context
[Category("Actions")]
public void Actions_CanHaveParametersOnComposites()
{
InputSystem.RegisterBindingComposite<CompositeWithParameters>();
InputSystem.RegisterBindingComposite<CoreTests.CompositeWithParameters>();

// NOTE: Enums aren't supported at the JSON level. The editor uses reflection to display textual names rather
// than plain integer values but underneath, enums are treated as ints.
Expand Down Expand Up @@ -10109,7 +10094,7 @@ public void Reset()
public void Actions_Vector2Composite_TriggersActionOnlyOnceWhenMultipleComponentBindingsTriggerInSingleEvent()
{
var keyboard = InputSystem.AddDevice<Keyboard>();
InputSystem.RegisterInteraction<LogInteraction>();
InputSystem.RegisterInteraction<CoreTests.LogInteraction>();

var action = new InputAction();
action.AddCompositeBinding("Dpad", interactions: "log")
Expand Down Expand Up @@ -10385,7 +10370,7 @@ public void Actions_WithMultipleComposites_CancelsIfCompositeIsReleased()
var keyboard = InputSystem.AddDevice<Keyboard>();
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterInteraction<LogInteraction>();
InputSystem.RegisterInteraction<CoreTests.LogInteraction>();

var action = new InputAction();
action.AddCompositeBinding("Dpad(normalize=0)")
Expand Down Expand Up @@ -10469,7 +10454,7 @@ public void Actions_CompositesReportControlThatTriggeredTheCompositeInCallback()
var keyboard = InputSystem.AddDevice<Keyboard>();
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterInteraction<LogInteraction>();
InputSystem.RegisterInteraction<CoreTests.LogInteraction>();

var action = new InputAction();
action.AddBinding("<Gamepad>/leftStick");
Expand Down Expand Up @@ -10526,7 +10511,7 @@ public void Actions_CompositesInDifferentMapsTiedToSameControlsWork()
var keyboard = InputSystem.AddDevice<Keyboard>();
var gamepad = InputSystem.AddDevice<Gamepad>();

InputSystem.RegisterInteraction<LogInteraction>();
InputSystem.RegisterInteraction<CoreTests.LogInteraction>();

var map1 = new InputActionMap("map1");
var action1 = map1.AddAction("action");
Expand Down Expand Up @@ -10595,7 +10580,7 @@ public override Vector2 ReadValue(ref InputBindingCompositeContext context)
[Category("Actions")]
public void Actions_CanCreateCompositeWithVector2PartBinding()
{
InputSystem.RegisterBindingComposite<CompositeWithVector2Part>();
InputSystem.RegisterBindingComposite<CoreTests.CompositeWithVector2Part>();
var gamepad = InputSystem.AddDevice<Gamepad>();

var action = new InputAction();
Expand Down Expand Up @@ -10630,7 +10615,7 @@ public override float ReadValue(ref InputBindingCompositeContext context)
[Category("Actions")]
public void Actions_CanGetSourceControlWhenReadingValueFromCompositePart()
{
InputSystem.RegisterBindingComposite<CompositeAskingForSourceControl>();
InputSystem.RegisterBindingComposite<CoreTests.CompositeAskingForSourceControl>();
var gamepad = InputSystem.AddDevice<Gamepad>();

var action = new InputAction();
Expand Down Expand Up @@ -11786,7 +11771,7 @@ public void Reset()
[Category("Actions")]
public void Actions_InteractionContextRespectsCustomDefaultStates()
{
InputSystem.RegisterInteraction<TestInteractionCheckingDefaultState>();
InputSystem.RegisterInteraction<CoreTests.TestInteractionCheckingDefaultState>();

const string json = @"
{
Expand Down Expand Up @@ -12264,7 +12249,7 @@ private class MonoBehaviourWithActionProperty : MonoBehaviour
public void Actions_Property_CanGetAction_WithNullReferenceType()
{
var go = new GameObject();
var component = go.AddComponent<MonoBehaviourWithActionProperty>();
var component = go.AddComponent<CoreTests.MonoBehaviourWithActionProperty>();
component.actionProperty = new InputActionProperty((InputActionReference)null);

Assert.DoesNotThrow(() => _ = component.actionProperty.action);
Expand All @@ -12278,7 +12263,7 @@ public void Actions_Property_CanGetAction_WithNullReferenceType()
public void Actions_Property_CanGetAction_WithNullActionType()
{
var go = new GameObject();
var component = go.AddComponent<MonoBehaviourWithActionProperty>();
var component = go.AddComponent<CoreTests.MonoBehaviourWithActionProperty>();
component.actionProperty = new InputActionProperty((InputAction)null);

Assert.DoesNotThrow(() => _ = component.actionProperty.action);
Expand All @@ -12300,7 +12285,7 @@ public void Actions_Property_CanGetAction_WithDestroyedReferenceType()
reference.Set(asset, "map", "action1");

var go = new GameObject();
var component = go.AddComponent<MonoBehaviourWithActionProperty>();
var component = go.AddComponent<CoreTests.MonoBehaviourWithActionProperty>();
component.actionProperty = new InputActionProperty(reference);

Assert.That(component.actionProperty.action, Is.Not.Null);
Expand Down Expand Up @@ -12396,7 +12381,7 @@ public struct PointerInput
public float? Twist;
}

public class PointerInputComposite : InputBindingComposite<PointerInput>
public class PointerInputComposite : InputBindingComposite<CoreTests.PointerInput>
{
[InputControl(layout = "Button")]
public int contact;
Expand All @@ -12419,7 +12404,7 @@ public class PointerInputComposite : InputBindingComposite<PointerInput>
[InputControl(layout = "Integer")]
public int inputId;

public override PointerInput ReadValue(ref InputBindingCompositeContext context)
public override CoreTests.PointerInput ReadValue(ref InputBindingCompositeContext context)
{
var contact = context.ReadValueAsButton(this.contact);
var pointerId = context.ReadValue<int>(inputId);
Expand All @@ -12429,7 +12414,7 @@ public override PointerInput ReadValue(ref InputBindingCompositeContext context)
var position = context.ReadValue<Vector2, Vector2MagnitudeComparer>(this.position);
var twist = context.ReadValue<float>(this.twist);

return new PointerInput
return new CoreTests.PointerInput
{
Contact = contact,
InputId = pointerId,
Expand All @@ -12449,7 +12434,7 @@ public override PointerInput ReadValue(ref InputBindingCompositeContext context)
[TestCase(false)]
public void Actions_WithMultipleCompositeBindings_WithoutEvaluateMagnitude_Works(bool prepopulateTouchesBeforeEnablingAction)
{
InputSystem.RegisterBindingComposite<PointerInputComposite>();
InputSystem.RegisterBindingComposite<CoreTests.PointerInputComposite>();

InputSystem.AddDevice<Touchscreen>();

Expand All @@ -12463,10 +12448,10 @@ public void Actions_WithMultipleCompositeBindings_WithoutEvaluateMagnitude_Works
.With("pressure", $"<Touchscreen>/touch{i}/pressure")
.With("inputId", $"<Touchscreen>/touch{i}/touchId");

var values = new List<PointerInput>();
action.started += ctx => values.Add(ctx.ReadValue<PointerInput>());
action.performed += ctx => values.Add(ctx.ReadValue<PointerInput>());
action.canceled += ctx => values.Add(ctx.ReadValue<PointerInput>());
var values = new List<CoreTests.PointerInput>();
action.started += ctx => values.Add(ctx.ReadValue<CoreTests.PointerInput>());
action.performed += ctx => values.Add(ctx.ReadValue<CoreTests.PointerInput>());
action.canceled += ctx => values.Add(ctx.ReadValue<CoreTests.PointerInput>());

if (!prepopulateTouchesBeforeEnablingAction) // normally actions are enabled before any control actuations happen
actionMap.Enable();
Expand Down
Loading
Loading