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;
}
}
}