using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; [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 DeviceKeyboard = "Keyboard"; private const string DeviceXbox = "Xbox"; private const string DevicePlayStation = "PlayStation"; private const string DeviceOther = "Other"; private static readonly InputDeviceWatcher.InputDeviceCategory[] KeyboardLookupOrder = { InputDeviceWatcher.InputDeviceCategory.Keyboard }; private static readonly InputDeviceWatcher.InputDeviceCategory[] XboxLookupOrder = { InputDeviceWatcher.InputDeviceCategory.Xbox, InputDeviceWatcher.InputDeviceCategory.Other, InputDeviceWatcher.InputDeviceCategory.Keyboard, }; private static readonly InputDeviceWatcher.InputDeviceCategory[] PlayStationLookupOrder = { InputDeviceWatcher.InputDeviceCategory.PlayStation, InputDeviceWatcher.InputDeviceCategory.Other, InputDeviceWatcher.InputDeviceCategory.Keyboard, }; private static readonly InputDeviceWatcher.InputDeviceCategory[] OtherLookupOrder = { InputDeviceWatcher.InputDeviceCategory.Other, InputDeviceWatcher.InputDeviceCategory.Xbox, InputDeviceWatcher.InputDeviceCategory.Keyboard, }; public List tables = new List(); public Sprite placeholderSprite; private Dictionary _tableCache; private Dictionary> _pathLookup; private void OnEnable() { BuildCache(); } #if UNITY_EDITOR private void OnValidate() { BuildCache(); } #endif public DeviceGlyphTable GetTable(string deviceName) { if (string.IsNullOrWhiteSpace(deviceName) || tables == null) { return null; } EnsureCache(); _tableCache.TryGetValue(deviceName.ToLowerInvariant(), out DeviceGlyphTable table); return table; } public DeviceGlyphTable GetTable(InputDeviceWatcher.InputDeviceCategory device) { switch (device) { case InputDeviceWatcher.InputDeviceCategory.Keyboard: return GetTable(DeviceKeyboard); case InputDeviceWatcher.InputDeviceCategory.Xbox: return GetTable(DeviceXbox); case InputDeviceWatcher.InputDeviceCategory.PlayStation: return GetTable(DevicePlayStation); default: return GetTable(DeviceOther) ?? GetTable(DeviceXbox); } } public Sprite GetPlatformIcon(InputDeviceWatcher.InputDeviceCategory device) { DeviceGlyphTable table = GetTable(device); return table != null ? table.platformIcons : null; } public bool TryGetSprite(string controlPath, InputDeviceWatcher.InputDeviceCategory device, out Sprite sprite) { EnsureCache(); string key = NormalizeControlPath(controlPath); if (string.IsNullOrEmpty(key)) { sprite = placeholderSprite; return sprite != null; } InputDeviceWatcher.InputDeviceCategory[] lookupOrder = GetLookupOrder(device); for (int i = 0; i < lookupOrder.Length; i++) { InputDeviceWatcher.InputDeviceCategory category = lookupOrder[i]; if (_pathLookup.TryGetValue(category, out Dictionary map) && map.TryGetValue(key, out sprite) && sprite != null) { return true; } } sprite = placeholderSprite; return sprite != null; } public Sprite FindSprite(string controlPath, InputDeviceWatcher.InputDeviceCategory device) { return TryGetSprite(controlPath, device, out Sprite sprite) ? sprite : placeholderSprite; } public GlyphEntry FindEntryByControlPath(string controlPath, InputDeviceWatcher.InputDeviceCategory device) { if (!TryGetSprite(controlPath, device, out Sprite sprite) || sprite == null) { return null; } InputDeviceWatcher.InputDeviceCategory[] lookupOrder = GetLookupOrder(device); for (int i = 0; i < lookupOrder.Length; i++) { DeviceGlyphTable table = GetTable(lookupOrder[i]); if (table == null || table.entries == null) { continue; } for (int j = 0; j < table.entries.Count; j++) { GlyphEntry entry = table.entries[j]; if (entry != null && entry.Sprite == sprite) { return entry; } } } return null; } private void EnsureCache() { if (_tableCache == null || _pathLookup == null) { BuildCache(); } } private void BuildCache() { _tableCache ??= new Dictionary(StringComparer.OrdinalIgnoreCase); _tableCache.Clear(); _pathLookup ??= new Dictionary>(); _pathLookup.Clear(); InitializeLookup(InputDeviceWatcher.InputDeviceCategory.Keyboard); InitializeLookup(InputDeviceWatcher.InputDeviceCategory.Xbox); InitializeLookup(InputDeviceWatcher.InputDeviceCategory.PlayStation); InitializeLookup(InputDeviceWatcher.InputDeviceCategory.Other); for (int i = 0; i < tables.Count; i++) { DeviceGlyphTable table = tables[i]; if (table == null || string.IsNullOrWhiteSpace(table.deviceName)) { continue; } _tableCache[table.deviceName.ToLowerInvariant()] = table; InputDeviceWatcher.InputDeviceCategory category = ParseCategory(table.deviceName); Dictionary map = _pathLookup[category]; RegisterEntries(table, map); } } private void InitializeLookup(InputDeviceWatcher.InputDeviceCategory category) { _pathLookup[category] = new Dictionary(StringComparer.OrdinalIgnoreCase); } private void RegisterEntries(DeviceGlyphTable table, Dictionary map) { if (table.entries == null) { return; } for (int i = 0; i < table.entries.Count; i++) { GlyphEntry entry = table.entries[i]; if (entry == null || entry.Sprite == null || entry.action == null) { continue; } for (int j = 0; j < entry.action.bindings.Count; j++) { RegisterBinding(map, entry.action.bindings[j].path, entry.Sprite); RegisterBinding(map, entry.action.bindings[j].effectivePath, entry.Sprite); } } } private void RegisterBinding(Dictionary map, string controlPath, Sprite sprite) { string key = NormalizeControlPath(controlPath); if (string.IsNullOrEmpty(key) || map.ContainsKey(key)) { return; } map[key] = sprite; } private static string NormalizeControlPath(string controlPath) { return string.IsNullOrWhiteSpace(controlPath) ? string.Empty : controlPath.Trim().ToLowerInvariant(); } private static InputDeviceWatcher.InputDeviceCategory ParseCategory(string deviceName) { if (string.IsNullOrWhiteSpace(deviceName)) { return InputDeviceWatcher.InputDeviceCategory.Other; } if (deviceName.Equals(DeviceKeyboard, StringComparison.OrdinalIgnoreCase)) { return InputDeviceWatcher.InputDeviceCategory.Keyboard; } if (deviceName.Equals(DeviceXbox, StringComparison.OrdinalIgnoreCase)) { return InputDeviceWatcher.InputDeviceCategory.Xbox; } if (deviceName.Equals(DevicePlayStation, StringComparison.OrdinalIgnoreCase)) { return InputDeviceWatcher.InputDeviceCategory.PlayStation; } return InputDeviceWatcher.InputDeviceCategory.Other; } private static InputDeviceWatcher.InputDeviceCategory[] GetLookupOrder(InputDeviceWatcher.InputDeviceCategory device) { switch (device) { case InputDeviceWatcher.InputDeviceCategory.Keyboard: return KeyboardLookupOrder; case InputDeviceWatcher.InputDeviceCategory.Xbox: return XboxLookupOrder; case InputDeviceWatcher.InputDeviceCategory.PlayStation: return PlayStationLookupOrder; default: return OtherLookupOrder; } } }