using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.LowLevel; namespace BaseGames.UI { /// /// 设备检测器 —— 监听 InputSystem 的事件流,识别玩家最后使用的输入设备类型, /// 并通过 InputDeviceTypeEventChannelSO 广播给全局。 /// /// 布置方式:挂在 UIRoot 或常驻 GameObject 上;只需存在一个实例。 /// public sealed class InputDeviceDetector : MonoBehaviour { [Header("Event Channel")] [Tooltip("广播当前设备类型变化")] [SerializeField] private InputDeviceTypeEventChannelSO _onDeviceChanged; /// 当前活跃输入设备类型,供轮询使用。 public InputDeviceType CurrentDevice { get; private set; } = InputDeviceType.KeyboardMouse; private void OnEnable() { // 监听所有输入事件:每次有任何 StateEvent/DeltaStateEvent 时触发 InputSystem.onEvent += OnInputSystemEvent; // 监听设备连接/断开(热插拔) InputSystem.onDeviceChange += OnDeviceChange; } private void OnDisable() { InputSystem.onEvent -= OnInputSystemEvent; InputSystem.onDeviceChange -= OnDeviceChange; } // ── Event Handlers ──────────────────────────────────────────────────── private void OnInputSystemEvent(InputEventPtr eventPtr, InputDevice device) { // 只关心真实输入事件,滤掉内部状态事件 if (!eventPtr.IsA() && !eventPtr.IsA()) return; var detected = ClassifyDevice(device); if (detected == CurrentDevice) return; CurrentDevice = detected; _onDeviceChanged?.Raise(CurrentDevice); } private void OnDeviceChange(InputDevice device, InputDeviceChange change) { // 当设备重新连接时重新检测(防止手柄拔插后图标仍显示手柄图标) if (change == InputDeviceChange.Reconnected || change == InputDeviceChange.Added) { // 保持当前 CurrentDevice 不变,等到实际输入事件再切换 } } // ── Device Classification ───────────────────────────────────────────── /// /// 根据 InputDevice 的布局层次识别设备类型。 /// Unity InputSystem 的设备层次: /// DualShockGamepad → Gamepad → HID /// XInputController → Gamepad → HID /// SwitchProControllerHID → Gamepad → HID /// Keyboard / Mouse /// private static InputDeviceType ClassifyDevice(InputDevice device) { if (device is Keyboard or Mouse) return InputDeviceType.KeyboardMouse; if (device is Gamepad gamepad) { var desc = gamepad.description; string manufacturer = desc.manufacturer ?? string.Empty; string product = desc.product ?? string.Empty; string interfaceName = desc.interfaceName ?? string.Empty; // PlayStation: DualShock 3/4 or DualSense (PS5) if (InputSystem.IsFirstLayoutBasedOnSecond(gamepad.layout, "DualShockGamepad") || product.Contains("DualShock", System.StringComparison.OrdinalIgnoreCase) || product.Contains("DualSense", System.StringComparison.OrdinalIgnoreCase) || manufacturer.Contains("Sony", System.StringComparison.OrdinalIgnoreCase)) return InputDeviceType.PlayStationController; // Nintendo Switch Pro Controller / Joy-Con if (InputSystem.IsFirstLayoutBasedOnSecond(gamepad.layout, "SwitchProControllerHID") || product.Contains("Switch", System.StringComparison.OrdinalIgnoreCase) || product.Contains("Joy-Con", System.StringComparison.OrdinalIgnoreCase) || manufacturer.Contains("Nintendo", System.StringComparison.OrdinalIgnoreCase)) return InputDeviceType.SwitchController; // Xbox / XInput (DirectInput 会走 HID 路径,XInput 走 XInputController) if (InputSystem.IsFirstLayoutBasedOnSecond(gamepad.layout, "XInputController") || product.Contains("Xbox", System.StringComparison.OrdinalIgnoreCase) || interfaceName.Equals("XInput", System.StringComparison.OrdinalIgnoreCase)) return InputDeviceType.XboxController; // 未知手柄 → 默认 Xbox 图标集 return InputDeviceType.XboxController; } // 无法识别 → 键鼠 return InputDeviceType.KeyboardMouse; } } }