1
This commit is contained in:
parent
b3f3f268bf
commit
623edbeed6
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 7f9ad1ce05574ba886146f9982e01142
|
|
||||||
timeCreated: 1764839428
|
|
||||||
@ -1,245 +0,0 @@
|
|||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.InputSystem;
|
|
||||||
|
|
||||||
public static class InputDeviceWatcher
|
|
||||||
{
|
|
||||||
public enum InputDeviceCategory
|
|
||||||
{
|
|
||||||
Keyboard,
|
|
||||||
Xbox,
|
|
||||||
PlayStation,
|
|
||||||
Other
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly float DebounceWindow = 1f;
|
|
||||||
public static InputDeviceCategory CurrentCategory = InputDeviceCategory.Keyboard;
|
|
||||||
public static string CurrentDeviceName = "";
|
|
||||||
|
|
||||||
private static InputAction _anyInputAction;
|
|
||||||
private static int _lastDeviceId = -1;
|
|
||||||
private static float _lastInputTime = -Mathf.Infinity;
|
|
||||||
|
|
||||||
private static InputDeviceCategory _lastEmittedCategory = InputDeviceCategory.Keyboard;
|
|
||||||
|
|
||||||
public static event Action<InputDeviceCategory> OnDeviceChanged;
|
|
||||||
|
|
||||||
private static bool initialized = false;
|
|
||||||
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
|
||||||
if (initialized) return;
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
CurrentCategory = InputDeviceCategory.Keyboard;
|
|
||||||
CurrentDeviceName = "";
|
|
||||||
_lastEmittedCategory = CurrentCategory; // 初始化同步
|
|
||||||
|
|
||||||
_anyInputAction = new InputAction("AnyDevice", InputActionType.PassThrough);
|
|
||||||
_anyInputAction.AddBinding("<Keyboard>/anyKey");
|
|
||||||
_anyInputAction.AddBinding("<Gamepad>/*");
|
|
||||||
_anyInputAction.AddBinding("<Joystick>/*");
|
|
||||||
|
|
||||||
_anyInputAction.performed += OnAnyInputPerformed;
|
|
||||||
_anyInputAction.Enable();
|
|
||||||
|
|
||||||
InputSystem.onDeviceChange += OnDeviceChange;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
static void OnPlayModeStateChanged(PlayModeStateChange state)
|
|
||||||
{
|
|
||||||
if (state == PlayModeStateChange.ExitingPlayMode)
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public static void Dispose()
|
|
||||||
{
|
|
||||||
if (!initialized) return;
|
|
||||||
CurrentCategory = InputDeviceCategory.Keyboard;
|
|
||||||
_anyInputAction.performed -= OnAnyInputPerformed;
|
|
||||||
_anyInputAction.Disable();
|
|
||||||
_anyInputAction.Dispose();
|
|
||||||
|
|
||||||
InputSystem.onDeviceChange -= OnDeviceChange;
|
|
||||||
|
|
||||||
OnDeviceChanged = null;
|
|
||||||
initialized = false;
|
|
||||||
|
|
||||||
_lastEmittedCategory = InputDeviceCategory.Keyboard;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnAnyInputPerformed(InputAction.CallbackContext ctx)
|
|
||||||
{
|
|
||||||
if (ctx.control == null || ctx.control.device == null) return;
|
|
||||||
|
|
||||||
var device = ctx.control.device;
|
|
||||||
|
|
||||||
if (!IsRelevantDevice(device)) return;
|
|
||||||
|
|
||||||
int curId = device.deviceId;
|
|
||||||
float now = Time.realtimeSinceStartup;
|
|
||||||
|
|
||||||
if (curId == _lastDeviceId) return;
|
|
||||||
if (DebounceWindow > 0f && (now - _lastInputTime) < DebounceWindow) return;
|
|
||||||
|
|
||||||
_lastInputTime = now;
|
|
||||||
_lastDeviceId = curId;
|
|
||||||
|
|
||||||
CurrentCategory = DetermineCategoryFromDevice(device);
|
|
||||||
CurrentDeviceName = device.displayName ?? $"Device_{curId}";
|
|
||||||
|
|
||||||
EmitChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnDeviceChange(InputDevice device, InputDeviceChange change)
|
|
||||||
{
|
|
||||||
if (change == InputDeviceChange.Removed || change == InputDeviceChange.Disconnected)
|
|
||||||
{
|
|
||||||
if (device.deviceId == _lastDeviceId)
|
|
||||||
{
|
|
||||||
_lastDeviceId = -1;
|
|
||||||
_lastInputTime = -Mathf.Infinity;
|
|
||||||
CurrentDeviceName = "";
|
|
||||||
CurrentCategory = InputDeviceCategory.Keyboard;
|
|
||||||
EmitChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------ 分类逻辑 --------------------
|
|
||||||
private static InputDeviceCategory DetermineCategoryFromDevice(InputDevice device)
|
|
||||||
{
|
|
||||||
if (device == null) return InputDeviceCategory.Keyboard;
|
|
||||||
// 重要:鼠标不再被视为键盘类(避免鼠标触发时回退到 Keyboard)
|
|
||||||
if (device is Keyboard) return InputDeviceCategory.Keyboard;
|
|
||||||
if (device is Mouse) return InputDeviceCategory.Other; // 明确忽略鼠标
|
|
||||||
if (IsGamepadLike(device)) return GetGamepadCategory(device);
|
|
||||||
|
|
||||||
string combined = $"{device.description.interfaceName} {device.layout} {device.description.product} {device.description.manufacturer} {device.displayName}".ToLower();
|
|
||||||
|
|
||||||
if (combined.Contains("xbox") || combined.Contains("xinput")) return InputDeviceCategory.Xbox;
|
|
||||||
if (combined.Contains("dualshock") || combined.Contains("dualsense") || combined.Contains("playstation")) return InputDeviceCategory.PlayStation;
|
|
||||||
|
|
||||||
return InputDeviceCategory.Other;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsGamepadLike(InputDevice device)
|
|
||||||
{
|
|
||||||
if (device is Gamepad) return true;
|
|
||||||
if (device is Joystick) return true;
|
|
||||||
|
|
||||||
var layout = (device.layout ?? "").ToLower();
|
|
||||||
// 这里保留 controller/gamepad/joystick 的识别,但忽略 mouse/touch 等
|
|
||||||
if (layout.Contains("mouse") || layout.Contains("touch") || layout.Contains("pen")) return false;
|
|
||||||
return layout.Contains("gamepad") || layout.Contains("controller") || layout.Contains("joystick");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsRelevantDevice(InputDevice device)
|
|
||||||
{
|
|
||||||
if (device == null) return false;
|
|
||||||
if (device is Keyboard) return true;
|
|
||||||
if (IsGamepadLike(device)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputDeviceCategory GetGamepadCategory(InputDevice device)
|
|
||||||
{
|
|
||||||
if (device == null) return InputDeviceCategory.Other;
|
|
||||||
|
|
||||||
var iface = (device.description.interfaceName ?? "").ToLower();
|
|
||||||
if (iface.Contains("xinput")) return InputDeviceCategory.Xbox;
|
|
||||||
|
|
||||||
if (TryParseVidPidFromCapabilities(device.description.capabilities, out int vendorId, out int _))
|
|
||||||
{
|
|
||||||
if (vendorId == 0x045E || vendorId == 1118) return InputDeviceCategory.Xbox;
|
|
||||||
if (vendorId == 0x054C || vendorId == 1356) return InputDeviceCategory.PlayStation;
|
|
||||||
}
|
|
||||||
|
|
||||||
string combined = $"{device.description.interfaceName} {device.layout} {device.description.product} {device.description.manufacturer} {device.displayName}".ToLower();
|
|
||||||
if (combined.Contains("xbox")) return InputDeviceCategory.Xbox;
|
|
||||||
if (combined.Contains("dualshock") || combined.Contains("playstation")) return InputDeviceCategory.PlayStation;
|
|
||||||
|
|
||||||
return InputDeviceCategory.Other;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------ VID/PID 解析 --------------------
|
|
||||||
private static bool TryParseVidPidFromCapabilities(string capabilities, out int vendorId, out int productId)
|
|
||||||
{
|
|
||||||
vendorId = 0;
|
|
||||||
productId = 0;
|
|
||||||
if (string.IsNullOrEmpty(capabilities)) return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var decVendor = Regex.Match(capabilities, "\"vendorId\"\\s*:\\s*(\\d+)", RegexOptions.IgnoreCase);
|
|
||||||
var decProduct = Regex.Match(capabilities, "\"productId\"\\s*:\\s*(\\d+)", RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
if (decVendor.Success) int.TryParse(decVendor.Groups[1].Value, out vendorId);
|
|
||||||
if (decProduct.Success) int.TryParse(decProduct.Groups[1].Value, out productId);
|
|
||||||
|
|
||||||
return vendorId != 0 || productId != 0;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitChange()
|
|
||||||
{
|
|
||||||
if (CurrentCategory == _lastEmittedCategory)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vid = GetVendorId();
|
|
||||||
int pid = GetProductId();
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
Debug.Log($"输入设备变更 -> {CurrentCategory} 触发设备: {CurrentDeviceName} vid=0x{vid:X} pid=0x{pid:X}");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
OnDeviceChanged?.Invoke(CurrentCategory);
|
|
||||||
_lastEmittedCategory = CurrentCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetVendorId()
|
|
||||||
{
|
|
||||||
foreach (var d in InputSystem.devices)
|
|
||||||
{
|
|
||||||
if ((d.displayName ?? "") == CurrentDeviceName &&
|
|
||||||
TryParseVidPidFromCapabilities(d.description.capabilities, out int v, out int _))
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetProductId()
|
|
||||||
{
|
|
||||||
foreach (var d in InputSystem.devices)
|
|
||||||
{
|
|
||||||
if ((d.displayName ?? "") == CurrentDeviceName &&
|
|
||||||
TryParseVidPidFromCapabilities(d.description.capabilities, out int _, out int p))
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e78f6224467e13742a70115f1942d941
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
Loading…
Reference in New Issue
Block a user