using System; using UnityEngine; using UnityEngine.InputSystem; public static class GlyphService { // 缓存的设备提示数组,避免内存分配 private static readonly string[] KeyboardHints = { "Keyboard", "Mouse" }; private static readonly string[] XboxHints = { "XInput", "Xbox", "Gamepad" }; private static readonly string[] PlayStationHints = { "DualShock", "DualSense", "PlayStation", "Gamepad" }; private static readonly char[] TrimChars = { '{', '}', '<', '>', '\'', '"' }; /// /// 获取输入图标数据库实例 /// public static InputGlyphDatabase Database { get { if (_database == null) { _database = Resources.Load("InputGlyphDatabase"); } return _database; } } private static InputGlyphDatabase _database; /// /// 获取输入操作的绑定控制路径 /// /// 输入操作 /// 复合部分名称(可选) /// 设备类型覆盖(可选) /// 绑定控制路径 public static string GetBindingControlPath(InputAction action, string compositePartName = null, InputDeviceWatcher.InputDeviceCategory? deviceOverride = null) { if (action == null) return string.Empty; var binding = GetBindingControl(action, compositePartName, deviceOverride); return binding.hasOverrides ? binding.effectivePath : binding.path; } /// /// 尝试获取输入操作的 TextMeshPro 标签 /// /// 输入操作引用 /// 复合部分名称 /// 设备类型 /// 输出的 TMP 标签 /// 显示回退文本 /// 数据库实例(可选) /// 是否成功获取 public static bool TryGetTMPTagForActionPath(InputAction reference, string compositePartName, InputDeviceWatcher.InputDeviceCategory device, out string tag, out string displayFallback, InputGlyphDatabase db = null) { string path = GetBindingControlPath(reference, compositePartName, device); return TryGetTMPTagForActionPath(path, device, out tag, out displayFallback, db); } /// /// 尝试获取输入操作的 UI Sprite /// /// 输入操作引用 /// 复合部分名称 /// 设备类型 /// 输出的 Sprite /// 数据库实例(可选) /// 是否成功获取 public static bool TryGetUISpriteForActionPath(InputAction reference, string compositePartName, InputDeviceWatcher.InputDeviceCategory device, out Sprite sprite, InputGlyphDatabase db = null) { string path = GetBindingControlPath(reference, compositePartName, device); return TryGetUISpriteForActionPath(path, device, out sprite, db); } /// /// 根据控制路径尝试获取 TextMeshPro 标签 /// /// 控制路径 /// 设备类型 /// 输出的 TMP 标签 /// 显示回退文本 /// 数据库实例(可选) /// 是否成功获取 public static bool TryGetTMPTagForActionPath(string controlPath, InputDeviceWatcher.InputDeviceCategory device, out string tag, out string displayFallback, InputGlyphDatabase db = null) { tag = null; displayFallback = null; db = db ?? Database; displayFallback = GetDisplayNameFromControlPath(controlPath); var sprite = db.FindSprite(controlPath, device) ?? db.FindSprite(controlPath, InputDeviceWatcher.InputDeviceCategory.Keyboard); var spriteName = sprite == null ? string.Empty : sprite.name; tag = $""; return true; } /// /// 根据控制路径尝试获取 UI Sprite /// /// 控制路径 /// 设备类型 /// 输出的 Sprite /// 数据库实例(可选) /// 是否成功获取 public static bool TryGetUISpriteForActionPath(string controlPath, InputDeviceWatcher.InputDeviceCategory device, out Sprite sprite, InputGlyphDatabase db = null) { sprite = null; db = db ?? Database; if (string.IsNullOrEmpty(controlPath) || db == null) return false; sprite = db.FindSprite(controlPath, device) ?? db.FindSprite(controlPath, InputDeviceWatcher.InputDeviceCategory.Keyboard); return sprite != null; } /// /// 获取输入操作的绑定控制 /// static InputBinding GetBindingControl(InputAction action, string compositePartName = null, InputDeviceWatcher.InputDeviceCategory? deviceOverride = null) { if (action == null) return default; var curCategory = deviceOverride ?? InputDeviceWatcher.CurrentCategory; var hints = GetDeviceHintsForCategory(curCategory); foreach (var b in action.bindings) { if (!string.IsNullOrEmpty(compositePartName)) { if (!b.isPartOfComposite) continue; if (!string.Equals(b.name, compositePartName, StringComparison.OrdinalIgnoreCase)) continue; } // 替换 LINQ Any() 以避免委托分配 if (!string.IsNullOrEmpty(b.path) && ContainsAnyHint(b.path, hints)) return b; if (!string.IsNullOrEmpty(b.effectivePath) && ContainsAnyHint(b.effectivePath, hints)) return b; } return default; } // 辅助方法,避免 LINQ Any() 的内存分配 /// /// 检查路径是否包含任何提示字符串 /// static bool ContainsAnyHint(string path, string[] hints) { for (int i = 0; i < hints.Length; i++) { if (path.IndexOf(hints[i], StringComparison.OrdinalIgnoreCase) >= 0) return true; } return false; } /// /// 根据设备类型获取设备提示字符串数组 /// static string[] GetDeviceHintsForCategory(InputDeviceWatcher.InputDeviceCategory cat) { switch (cat) { case InputDeviceWatcher.InputDeviceCategory.Keyboard: return KeyboardHints; case InputDeviceWatcher.InputDeviceCategory.Xbox: return XboxHints; case InputDeviceWatcher.InputDeviceCategory.PlayStation: return PlayStationHints; default: return XboxHints; } } /// /// 从输入操作获取显示名称 /// /// 输入操作 /// 复合部分名称(可选) /// 设备类型覆盖 /// 显示名称 public static string GetDisplayNameFromInputAction(InputAction action, string compositePartName = null, InputDeviceWatcher.InputDeviceCategory deviceOverride = InputDeviceWatcher.InputDeviceCategory.Keyboard) { if (action == null) return string.Empty; var binding = GetBindingControl(action, compositePartName, deviceOverride); return binding.ToDisplayString(); } /// /// 从控制路径获取显示名称 /// /// 控制路径 /// 显示名称 public static string GetDisplayNameFromControlPath(string controlPath) { if (string.IsNullOrEmpty(controlPath)) return string.Empty; var parts = controlPath.Split('/'); var last = parts[parts.Length - 1].Trim(TrimChars); return last; } }