AlicizaX/Client/Assets/InputGlyph/InputGlyphDatabase.cs
2026-03-09 20:38:15 +08:00

189 lines
5.2 KiB
C#

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<GlyphEntry> entries = new List<GlyphEntry>();
}
[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<DeviceGlyphTable> tables = new List<DeviceGlyphTable>();
// 当 FindEntryByControlPath 传空 path 时返回的占位 sprite
public Sprite placeholderSprite;
// Cache for faster lookups
private Dictionary<string, DeviceGlyphTable> _tableCache;
private Dictionary<(string path, InputDeviceWatcher.InputDeviceCategory device), Sprite> _spriteCache;
private void OnEnable()
{
BuildCache();
}
private void BuildCache()
{
if (_tableCache == null)
{
_tableCache = new Dictionary<string, DeviceGlyphTable>(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;
// Ensure cache is built
if (_tableCache == null || _tableCache.Count == 0)
{
BuildCache();
}
// Use cache for O(1) lookup
if (_tableCache.TryGetValue(deviceName.ToLowerInvariant(), out var table))
{
return table;
}
return null;
}
public Sprite GetPlatformIcon(InputDeviceWatcher.InputDeviceCategory device)
{
var table = GetTable(device);
if (table == null) return null;
return table.platformIcons;
}
public DeviceGlyphTable GetTable(InputDeviceWatcher.InputDeviceCategory device)
{
// Use constants to avoid string allocations
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);
}
public Sprite FindSprite(string controlPath, InputDeviceWatcher.InputDeviceCategory device)
{
if (string.IsNullOrEmpty(controlPath))
{
return placeholderSprite;
}
// Check cache first
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;
// Cache the result (including null results to avoid repeated lookups)
if (_spriteCache != null)
{
_spriteCache[cacheKey] = sprite;
}
return sprite;
}
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;
}
}