using System; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.U2D; [Serializable] public sealed class GlyphEntry { public Sprite Sprite; public InputAction action; } [Serializable] public sealed class DeviceGlyphTable { public string deviceName; public Texture2D spriteSheetTexture; public Sprite platformIcons; public List entries = new List(); } [CreateAssetMenu(fileName = "InputGlyphDatabase", menuName = "InputGlyphs/InputGlyphDatabase", order = 400)] public sealed class InputGlyphDatabase : ScriptableObject { private const string DEVICE_KEYBOARD = "Keyboard"; private const string DEVICE_XBOX = "Xbox"; private const string DEVICE_PLAYSTATION = "PlayStation"; public List tables = new List(); // 当 FindEntryByControlPath 传空 path 时返回的占位 sprite public Sprite placeholderSprite; // 用于更快查找的缓存 private Dictionary _tableCache; private Dictionary<(string path, InputDeviceWatcher.InputDeviceCategory device), Sprite> _spriteCache; /// /// 启用时构建缓存 /// private void OnEnable() { BuildCache(); } /// /// 构建表和精灵的查找缓存 /// private void BuildCache() { if (_tableCache == null) { _tableCache = new Dictionary(tables.Count); } else { _tableCache.Clear(); } if (_spriteCache == null) { _spriteCache = new Dictionary<(string, InputDeviceWatcher.InputDeviceCategory), Sprite>(); } else { _spriteCache.Clear(); } for (int i = 0; i < tables.Count; i++) { var table = tables[i]; if (table != null && !string.IsNullOrEmpty(table.deviceName)) { _tableCache[table.deviceName.ToLowerInvariant()] = table; } } } /// /// 根据设备名称获取设备图标表 /// /// 设备名称 /// 设备图标表 public DeviceGlyphTable GetTable(string deviceName) { if (string.IsNullOrEmpty(deviceName)) return null; if (tables == null) return null; // 确保缓存已构建 if (_tableCache == null || _tableCache.Count == 0) { BuildCache(); } // 使用缓存进行 O(1) 查找 if (_tableCache.TryGetValue(deviceName.ToLowerInvariant(), out var table)) { return table; } return null; } /// /// 获取平台图标 /// /// 设备类型 /// 平台图标 Sprite public Sprite GetPlatformIcon(InputDeviceWatcher.InputDeviceCategory device) { var table = GetTable(device); if (table == null) return null; return table.platformIcons; } /// /// 根据设备类型获取设备图标表 /// /// 设备类型 /// 设备图标表 public DeviceGlyphTable GetTable(InputDeviceWatcher.InputDeviceCategory device) { // 使用常量避免字符串分配 string name; switch (device) { case InputDeviceWatcher.InputDeviceCategory.Keyboard: name = DEVICE_KEYBOARD; break; case InputDeviceWatcher.InputDeviceCategory.Xbox: name = DEVICE_XBOX; break; case InputDeviceWatcher.InputDeviceCategory.PlayStation: name = DEVICE_PLAYSTATION; break; default: name = DEVICE_XBOX; break; } return GetTable(name); } /// /// 根据控制路径和设备类型查找 Sprite /// /// 控制路径 /// 设备类型 /// 找到的 Sprite,未找到则返回占位 Sprite public Sprite FindSprite(string controlPath, InputDeviceWatcher.InputDeviceCategory device) { if (string.IsNullOrEmpty(controlPath)) { return placeholderSprite; } // 首先检查缓存 var cacheKey = (controlPath, device); if (_spriteCache != null && _spriteCache.TryGetValue(cacheKey, out var cachedSprite)) { return cachedSprite ?? placeholderSprite; } var entry = FindEntryByControlPath(controlPath, device); var sprite = entry?.Sprite ?? placeholderSprite; // 缓存结果(包括 null 结果以避免重复查找) if (_spriteCache != null) { _spriteCache[cacheKey] = sprite; } return sprite; } /// /// 根据控制路径和设备类型查找图标条目 /// /// 控制路径 /// 设备类型 /// 找到的图标条目,未找到则返回 null public GlyphEntry FindEntryByControlPath(string controlPath, InputDeviceWatcher.InputDeviceCategory device) { var t = GetTable(device); if (t != null && t.entries != null) { for (int i = 0; i < t.entries.Count; ++i) { var e = t.entries[i]; if (e == null) continue; if (e.action == null) continue; var bindings = e.action.bindings; int bindingCount = bindings.Count; if (bindingCount <= 0) continue; for (int j = 0; j < bindingCount; j++) { var b = bindings[j]; if (!string.IsNullOrEmpty(b.path) && string.Equals(b.path, controlPath, StringComparison.OrdinalIgnoreCase)) { return e; } if (!string.IsNullOrEmpty(b.effectivePath) && string.Equals(b.effectivePath, controlPath, StringComparison.OrdinalIgnoreCase)) { return e; } } } } return null; } }