From ea1676c433becb9fea7118e3f81690bd9a93e0a0 Mon Sep 17 00:00:00 2001 From: Val Knight Date: Tue, 29 Jul 2025 15:53:46 +0100 Subject: [PATCH 1/9] Add new SwitchProControlleriOS device for iOS, which has swapped face buttons Also includes new state, for any devices which use the Nintendo-y button bindings --- .../Plugins/iOS/IOSGameController.cs | 97 +++++++++++++++++++ .../InputSystem/Plugins/iOS/iOSSupport.cs | 6 ++ 2 files changed, 103 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs index 5e65219e78..98062d04e5 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs @@ -1,5 +1,6 @@ #if UNITY_EDITOR || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS || PACKAGE_DOCS_GENERATION using System.Runtime.InteropServices; +using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.DualShock; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.LowLevel; @@ -95,6 +96,66 @@ public iOSGameControllerState WithAxis(iOSAxis axis, float value) return this; } } + + /// + /// State for iOS Gamepads using a layout where B button is south, A is east, X is north, and Y is west + /// This layout is typically seen on Nintendo gamepads, such as the Switch Pro Controller. + /// + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct iOSGameControllerStateSwappedFaceButtons : IInputStateTypeInfo + { + public static FourCC kFormat = new FourCC('I', 'G', 'C', ' '); + public const int MaxButtons = (int)iOSButton.Select + 1; + public const int MaxAxis = (int)iOSAxis.RightStickY + 1; + + [InputControl(name = "dpad")] + [InputControl(name = "dpad/up", bit = (uint)iOSButton.DpadUp)] + [InputControl(name = "dpad/right", bit = (uint)iOSButton.DpadRight)] + [InputControl(name = "dpad/down", bit = (uint)iOSButton.DpadDown)] + [InputControl(name = "dpad/left", bit = (uint)iOSButton.DpadLeft)] + [InputControl(name = "buttonSouth", bit = (uint)iOSButton.B)] + [InputControl(name = "buttonWest", bit = (uint)iOSButton.Y)] + [InputControl(name = "buttonNorth", bit = (uint)iOSButton.X)] + [InputControl(name = "buttonEast", bit = (uint)iOSButton.A)] + [InputControl(name = "leftStickPress", bit = (uint)iOSButton.LeftStick)] + [InputControl(name = "rightStickPress", bit = (uint)iOSButton.RightStick)] + [InputControl(name = "leftShoulder", bit = (uint)iOSButton.LeftShoulder)] + [InputControl(name = "rightShoulder", bit = (uint)iOSButton.RightShoulder)] + [InputControl(name = "start", bit = (uint)iOSButton.Start)] + [InputControl(name = "select", bit = (uint)iOSButton.Select)] + public uint buttons; + + [InputControl(name = "leftTrigger", offset = sizeof(uint) + sizeof(float) * (uint)iOSButton.LeftTrigger)] + [InputControl(name = "rightTrigger", offset = sizeof(uint) + sizeof(float) * (uint)iOSButton.RightTrigger)] + public fixed float buttonValues[MaxButtons]; + + private const uint kAxisOffset = sizeof(uint) + sizeof(float) * MaxButtons; + [InputControl(name = "leftStick", offset = (uint)iOSAxis.LeftStickX * sizeof(float) + kAxisOffset)] + [InputControl(name = "rightStick", offset = (uint)iOSAxis.RightStickX * sizeof(float) + kAxisOffset)] + public fixed float axisValues[MaxAxis]; + + public FourCC format => kFormat; + + public iOSGameControllerStateSwappedFaceButtons WithButton(iOSButton button, bool value = true, float rawValue = 1.0f) + { + buttonValues[(int)button] = rawValue; + + Debug.Assert((int)button < 32, $"Expected button < 32, so we fit into the 32 bit wide bitmask"); + var bit = 1U << (int)button; + if (value) + buttons |= bit; + else + buttons &= ~bit; + + return this; + } + + public iOSGameControllerStateSwappedFaceButtons WithAxis(iOSAxis axis, float value) + { + axisValues[(int)axis] = value; + return this; + } + } } namespace UnityEngine.InputSystem.iOS @@ -134,5 +195,41 @@ public class DualShock4GampadiOS : DualShockGamepad public class DualSenseGampadiOS : DualShockGamepad { } + + /// + /// A Switch Pro Controller connected to an iOS device. + /// If you use InputSystem.GetDevice, you must query for this class rather than Gamepad in order for aButton, bButton, yButton and xButton to be correct + /// + [InputControlLayout(stateType = typeof(iOSGameControllerStateSwappedFaceButtons), displayName = "iOS Switch Pro Controller Gamepad")] + public class SwitchProControlleriOS : Gamepad + { + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonSouth, whereas this class returns the state of buttonEast + /// + public new ButtonControl aButton => buttonEast; + + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonEast, whereas this class returns the state of buttonSouth + /// + public new ButtonControl bButton => buttonSouth; + + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonNorth, whereas this class returns the state of buttonWest + /// + public new ButtonControl yButton => buttonWest; + + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonWest, whereas this class returns the state of buttonNorth + /// + public new ButtonControl xButton => buttonNorth; + } } #endif // UNITY_EDITOR || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs index 981bac4fad..cb7fc3b019 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs @@ -37,6 +37,12 @@ public static void Initialize() .WithDeviceClass("iOSGameController") .WithProduct("DualSense Wireless Controller")); + InputSystem.RegisterLayout("SwitchProGamepadiOS", + matches: new InputDeviceMatcher() + .WithInterface("iOS") + .WithDeviceClass("iOSGameController") + .WithProduct("Pro Controller")); + InputSystem.RegisterLayoutMatcher("GravitySensor", new InputDeviceMatcher() .WithInterface("iOS") From 9ab29ab610f9e416faa29ba4ed1d68e6ff11640a Mon Sep 17 00:00:00 2001 From: Val Knight Date: Tue, 29 Jul 2025 15:54:11 +0100 Subject: [PATCH 2/9] Correct Switch Pro Controller button bindings for HID desktop --- .../Plugins/Switch/SwitchProControllerHID.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs index 6a619301da..8321deb128 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs @@ -155,6 +155,34 @@ public class SwitchProControllerHID : Gamepad, IInputStateCallbackReceiver, IEve [InputControl(name = "home", displayName = "Home")] public ButtonControl homeButton { get; protected set; } + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonSouth, whereas this class returns the state of buttonEast + /// + public new ButtonControl aButton => buttonEast; + + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonEast, whereas this class returns the state of buttonSouth + /// + public new ButtonControl bButton => buttonSouth; + + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonNorth, whereas this class returns the state of buttonWest + /// + public new ButtonControl yButton => buttonWest; + + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonWest, whereas this class returns the state of buttonNorth + /// + public new ButtonControl xButton => buttonNorth; + protected override void OnAdded() { base.OnAdded(); From 2908eeeb39587d8e52f317c07e1412dbdbd00664 Mon Sep 17 00:00:00 2001 From: Val Knight Date: Tue, 29 Jul 2025 15:55:39 +0100 Subject: [PATCH 3/9] Add tests for Switch face buttons (hid + ios) --- Assets/Tests/InputSystem/Plugins/iOSTests.cs | 47 ++++++++++++++++++++ Assets/Tests/InputSystem/SwitchTests.cs | 4 ++ 2 files changed, 51 insertions(+) diff --git a/Assets/Tests/InputSystem/Plugins/iOSTests.cs b/Assets/Tests/InputSystem/Plugins/iOSTests.cs index 941e7072ed..84564b1733 100644 --- a/Assets/Tests/InputSystem/Plugins/iOSTests.cs +++ b/Assets/Tests/InputSystem/Plugins/iOSTests.cs @@ -56,13 +56,60 @@ public void Devices_SupportsiOSGamePad(string product, Type deviceType, Type par Assert.That(gamepad.rightTrigger.ReadValue(), Is.EqualTo(0.456).Within(0.000001)); AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.A), gamepad.buttonSouth); + AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.A), gamepad.aButton); AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.X), gamepad.buttonWest); + AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.X), gamepad.xButton); AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.Y), gamepad.buttonNorth); + AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.Y), gamepad.yButton); AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.B), gamepad.buttonEast); + AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.B), gamepad.bButton); AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.LeftShoulder), gamepad.leftShoulder); AssertButtonPress(gamepad, new iOSGameControllerState().WithButton(iOSButton.RightShoulder), gamepad.rightShoulder); } + [Test] + [Category("Devices")] + // this is a new test, as we need to assert the Nintendo layout (e.g. buttonSouth == B button) + public void Devices_SupportsProControlleriOS() + { + var device = InputSystem.AddDevice( + new InputDeviceDescription + { + interfaceName = "iOS", + deviceClass = "iOSGameController", + product = "Pro Controller" + }); + Assert.That(device, Is.TypeOf(typeof(SwitchProControlleriOS))); + Assert.That(device, Is.InstanceOf(typeof(Gamepad))); + + var gamepad = (SwitchProControlleriOS)device; + + InputSystem.QueueStateEvent(gamepad, + new iOSGameControllerStateSwappedFaceButtons() + .WithButton(iOSButton.LeftTrigger, true, 0.123f) + .WithButton(iOSButton.RightTrigger, true, 0.456f) + .WithAxis(iOSAxis.LeftStickX, 0.789f) + .WithAxis(iOSAxis.LeftStickY, 0.987f) + .WithAxis(iOSAxis.RightStickX, 0.654f) + .WithAxis(iOSAxis.RightStickY, 0.321f)); + InputSystem.Update(); + + var leftStickDeadzone = gamepad.leftStick.TryGetProcessor(); + var rightStickDeadzone = gamepad.leftStick.TryGetProcessor(); + + Assert.That(gamepad.leftStick.ReadValue(), Is.EqualTo(leftStickDeadzone.Process(new Vector2(0.789f, 0.987f)))); + Assert.That(gamepad.rightStick.ReadValue(), Is.EqualTo(rightStickDeadzone.Process(new Vector2(0.654f, 0.321f)))); + Assert.That(gamepad.leftTrigger.ReadValue(), Is.EqualTo(0.123).Within(0.000001)); + Assert.That(gamepad.rightTrigger.ReadValue(), Is.EqualTo(0.456).Within(0.000001)); + // testing for Pro Controller layout... + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.A), gamepad.buttonEast); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.X), gamepad.buttonNorth); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.Y), gamepad.buttonWest); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.B), gamepad.buttonSouth); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.LeftShoulder), gamepad.leftShoulder); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.RightShoulder), gamepad.rightShoulder); + } + [Test] [Category("Devices")] [TestCase("Gravity", typeof(GravitySensor))] diff --git a/Assets/Tests/InputSystem/SwitchTests.cs b/Assets/Tests/InputSystem/SwitchTests.cs index 5828480b08..b82287a81e 100644 --- a/Assets/Tests/InputSystem/SwitchTests.cs +++ b/Assets/Tests/InputSystem/SwitchTests.cs @@ -56,9 +56,13 @@ public void Devices_SupportsHIDNpad() Assert.That(currentRight, Is.EqualTo(expectedRight).Using(new Vector2EqualityComparer(0.01f))); AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.A), controller.buttonEast); + AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.A), controller.aButton); AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.B), controller.buttonSouth); + AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.B), controller.bButton); AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.X), controller.buttonNorth); + AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.X), controller.xButton); AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.Y), controller.buttonWest); + AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.Y), controller.yButton); AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.StickL), controller.leftStickButton); AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.StickR), controller.rightStickButton); AssertButtonPress(controller, StateWithButton(SwitchProControllerHIDInputState.Button.L), controller.leftShoulder); From 50d38242b26273d4745bb8af3480c42988ec95c7 Mon Sep 17 00:00:00 2001 From: Val Knight Date: Tue, 29 Jul 2025 16:29:06 +0100 Subject: [PATCH 4/9] Fix formatting --- .../InputSystem/Plugins/Switch/SwitchProControllerHID.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs index 8321deb128..8a0bf09c80 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs @@ -161,21 +161,21 @@ public class SwitchProControllerHID : Gamepad, IInputStateCallbackReceiver, IEve /// The gamepad class will return the state of buttonSouth, whereas this class returns the state of buttonEast /// public new ButtonControl aButton => buttonEast; - + /// /// A Button for a Nintendo Switch Pro Controller. /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. /// The gamepad class will return the state of buttonEast, whereas this class returns the state of buttonSouth /// public new ButtonControl bButton => buttonSouth; - + /// /// A Button for a Nintendo Switch Pro Controller. /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. /// The gamepad class will return the state of buttonNorth, whereas this class returns the state of buttonWest /// public new ButtonControl yButton => buttonWest; - + /// /// A Button for a Nintendo Switch Pro Controller. /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. From 95df53da84fa557863385b6ad091601b04bc447b Mon Sep 17 00:00:00 2001 From: Val Knight Date: Thu, 31 Jul 2025 12:33:16 +0100 Subject: [PATCH 5/9] Update changelog --- Packages/com.unity.inputsystem/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 5d522c7ffd..aadcbca1f5 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -13,6 +13,9 @@ however, it has to be formatted properly to pass verification tests. ### Added - Exposed MediaPlayPause, MediaRewind, MediaForward keys on Keyboard. +### Fixed +- Fixed `buttonSouth` returning the state of the east button (and so on for all the buttons) when using a Nintendo Switch Pro Controller on iOS [ISXB-1632](issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1632) + ## [1.14.2] - 2025-08-05 ### Fixed From 9bee64719fe49e7790b3c219c6c6818936563fd0 Mon Sep 17 00:00:00 2001 From: Val Knight Date: Thu, 31 Jul 2025 12:44:09 +0100 Subject: [PATCH 6/9] Add test coverage for aButton & friends on iOS --- Assets/Tests/InputSystem/Plugins/iOSTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Assets/Tests/InputSystem/Plugins/iOSTests.cs b/Assets/Tests/InputSystem/Plugins/iOSTests.cs index 84564b1733..50ff4c11de 100644 --- a/Assets/Tests/InputSystem/Plugins/iOSTests.cs +++ b/Assets/Tests/InputSystem/Plugins/iOSTests.cs @@ -103,9 +103,13 @@ public void Devices_SupportsProControlleriOS() Assert.That(gamepad.rightTrigger.ReadValue(), Is.EqualTo(0.456).Within(0.000001)); // testing for Pro Controller layout... AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.A), gamepad.buttonEast); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.A), gamepad.aButton); AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.X), gamepad.buttonNorth); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.X), gamepad.xButton); AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.Y), gamepad.buttonWest); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.Y), gamepad.yButton); AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.B), gamepad.buttonSouth); + AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.B), gamepad.bButton); AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.LeftShoulder), gamepad.leftShoulder); AssertButtonPress(gamepad, new iOSGameControllerStateSwappedFaceButtons().WithButton(iOSButton.RightShoulder), gamepad.rightShoulder); } From f8f6aa42f1bd8e8d263f60a646b64fe03d503db8 Mon Sep 17 00:00:00 2001 From: Val Knight Date: Fri, 1 Aug 2025 17:50:39 +0100 Subject: [PATCH 7/9] Rename test method for Switch Pro Controller support --- Assets/Tests/InputSystem/Plugins/iOSTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Tests/InputSystem/Plugins/iOSTests.cs b/Assets/Tests/InputSystem/Plugins/iOSTests.cs index 50ff4c11de..44328347cb 100644 --- a/Assets/Tests/InputSystem/Plugins/iOSTests.cs +++ b/Assets/Tests/InputSystem/Plugins/iOSTests.cs @@ -70,7 +70,7 @@ public void Devices_SupportsiOSGamePad(string product, Type deviceType, Type par [Test] [Category("Devices")] // this is a new test, as we need to assert the Nintendo layout (e.g. buttonSouth == B button) - public void Devices_SupportsProControlleriOS() + public void Devices_SupportsSwitchProControlleriOS() { var device = InputSystem.AddDevice( new InputDeviceDescription From 0f0bf61af775153ec7480bc5207aaf840f98edb5 Mon Sep 17 00:00:00 2001 From: Val Knight Date: Mon, 4 Aug 2025 12:03:30 +0100 Subject: [PATCH 8/9] Refactor to have new SwitchProController Gamepad with remapped ABXY buttons --- Assets/Tests/InputSystem/Plugins/iOSTests.cs | 1 + Assets/Tests/InputSystem/SwitchTests.cs | 1 + .../InputSystem/Devices/Gamepad.cs | 35 +++++++++++++++++++ .../Plugins/Switch/SwitchProControllerHID.cs | 30 +--------------- .../Plugins/iOS/IOSGameController.cs | 31 ++-------------- 5 files changed, 40 insertions(+), 58 deletions(-) diff --git a/Assets/Tests/InputSystem/Plugins/iOSTests.cs b/Assets/Tests/InputSystem/Plugins/iOSTests.cs index 44328347cb..eef11eb36a 100644 --- a/Assets/Tests/InputSystem/Plugins/iOSTests.cs +++ b/Assets/Tests/InputSystem/Plugins/iOSTests.cs @@ -80,6 +80,7 @@ public void Devices_SupportsSwitchProControlleriOS() product = "Pro Controller" }); Assert.That(device, Is.TypeOf(typeof(SwitchProControlleriOS))); + Assert.That(device, Is.InstanceOf(typeof(SwitchProController))); Assert.That(device, Is.InstanceOf(typeof(Gamepad))); var gamepad = (SwitchProControlleriOS)device; diff --git a/Assets/Tests/InputSystem/SwitchTests.cs b/Assets/Tests/InputSystem/SwitchTests.cs index b82287a81e..4d122458ec 100644 --- a/Assets/Tests/InputSystem/SwitchTests.cs +++ b/Assets/Tests/InputSystem/SwitchTests.cs @@ -172,6 +172,7 @@ public void Devices_SupportsSwitchLikeControllers(int vendorId, int productId) }); Assert.That(device, Is.TypeOf()); + Assert.That(device, Is.InstanceOf(typeof(SwitchProController))); } #endif diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Gamepad.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Gamepad.cs index 3841c4d723..df285ca9dc 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Gamepad.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Gamepad.cs @@ -806,4 +806,39 @@ public virtual void SetMotorSpeeds(float lowFrequency, float highFrequency) private static int s_GamepadCount; private static Gamepad[] s_Gamepads; } + + /// + /// Base class for Nintendo Switch Pro Controllers that provides the correct button mappings for Nintendo's face button layout where A is east, B is south, X is north, and Y is west. + /// If you use InputSystem.GetDevice and the ABXY properties to represent the labels on the device, you must query for this class + /// + public abstract class SwitchProController : Gamepad + { + /// + /// A Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to a Switch Pro Controller class, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonSouth, whereas this class returns the state of buttonEast + /// + public new ButtonControl aButton => buttonEast; + + /// + /// B Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to a Switch Pro Controller class, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonEast, whereas this class returns the state of buttonSouth + /// + public new ButtonControl bButton => buttonSouth; + + /// + /// Y Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to a Switch Pro Controller class, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonNorth, whereas this class returns the state of buttonWest + /// + public new ButtonControl yButton => buttonWest; + + /// + /// X Button for a Nintendo Switch Pro Controller. + /// If querying via script, ensure you cast the device to a Switch Pro Controller class, rather than using the Gamepad class. + /// The gamepad class will return the state of buttonWest, whereas this class returns the state of buttonNorth + /// + public new ButtonControl xButton => buttonNorth; + } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs index 8a0bf09c80..ec43b1195c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs @@ -147,7 +147,7 @@ namespace UnityEngine.InputSystem.Switch /// A Nintendo Switch Pro controller connected to a desktop mac/windows PC using the HID interface. /// [InputControlLayout(stateType = typeof(SwitchProControllerHIDInputState), displayName = "Switch Pro Controller")] - public class SwitchProControllerHID : Gamepad, IInputStateCallbackReceiver, IEventPreProcessor + public class SwitchProControllerHID : SwitchProController, IInputStateCallbackReceiver, IEventPreProcessor { [InputControl(name = "capture", displayName = "Capture")] public ButtonControl captureButton { get; protected set; } @@ -155,34 +155,6 @@ public class SwitchProControllerHID : Gamepad, IInputStateCallbackReceiver, IEve [InputControl(name = "home", displayName = "Home")] public ButtonControl homeButton { get; protected set; } - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonSouth, whereas this class returns the state of buttonEast - /// - public new ButtonControl aButton => buttonEast; - - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonEast, whereas this class returns the state of buttonSouth - /// - public new ButtonControl bButton => buttonSouth; - - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonNorth, whereas this class returns the state of buttonWest - /// - public new ButtonControl yButton => buttonWest; - - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControllerHID, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonWest, whereas this class returns the state of buttonNorth - /// - public new ButtonControl xButton => buttonNorth; - protected override void OnAdded() { base.OnAdded(); diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs index 98062d04e5..4d34274386 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/IOSGameController.cs @@ -198,38 +198,11 @@ public class DualSenseGampadiOS : DualShockGamepad /// /// A Switch Pro Controller connected to an iOS device. - /// If you use InputSystem.GetDevice, you must query for this class rather than Gamepad in order for aButton, bButton, yButton and xButton to be correct + /// If you use InputSystem.GetDevice, you must query for SwitchProControlleriOS rather than Gamepad in order for aButton, bButton, yButton and xButton to be correct /// [InputControlLayout(stateType = typeof(iOSGameControllerStateSwappedFaceButtons), displayName = "iOS Switch Pro Controller Gamepad")] - public class SwitchProControlleriOS : Gamepad + public class SwitchProControlleriOS : SwitchProController { - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonSouth, whereas this class returns the state of buttonEast - /// - public new ButtonControl aButton => buttonEast; - - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonEast, whereas this class returns the state of buttonSouth - /// - public new ButtonControl bButton => buttonSouth; - - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonNorth, whereas this class returns the state of buttonWest - /// - public new ButtonControl yButton => buttonWest; - - /// - /// A Button for a Nintendo Switch Pro Controller. - /// If querying via script, ensure you cast the device to SwitchProControlleriOS, rather than using the Gamepad class. - /// The gamepad class will return the state of buttonWest, whereas this class returns the state of buttonNorth - /// - public new ButtonControl xButton => buttonNorth; } } #endif // UNITY_EDITOR || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS From c5d6f72bf4567ba7355141c4efe3eef1df951dcf Mon Sep 17 00:00:00 2001 From: Val Knight Date: Mon, 4 Aug 2025 12:10:35 +0100 Subject: [PATCH 9/9] [Rebase on develop] Updated changelog to address reviewer feedback --- Packages/com.unity.inputsystem/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index aadcbca1f5..a33561c194 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -14,7 +14,8 @@ however, it has to be formatted properly to pass verification tests. - Exposed MediaPlayPause, MediaRewind, MediaForward keys on Keyboard. ### Fixed -- Fixed `buttonSouth` returning the state of the east button (and so on for all the buttons) when using a Nintendo Switch Pro Controller on iOS [ISXB-1632](issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1632) +- Fixed `buttonSouth` returning the state of the east button (and so on for all the compass named buttons) when using a Nintendo Switch Pro Controller on iOS [ISXB-1632](issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1632) +- Fixed `aButton` returning the state of the east button (and so on for all the letter named buttons) when using a Nintendo Switch Pro Controller on Standalone & iOS [ISXB-1632](issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1632) ## [1.14.2] - 2025-08-05