360 lines
11 KiB
C#
360 lines
11 KiB
C#
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<GlyphEntry> entries = new List<GlyphEntry>();
|
|
}
|
|
|
|
[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,
|
|
};
|
|
private static readonly Dictionary<string, string> NormalizedPathCache = new(StringComparer.Ordinal);
|
|
|
|
public List<DeviceGlyphTable> tables = new List<DeviceGlyphTable>();
|
|
public Sprite placeholderSprite;
|
|
|
|
private Dictionary<string, DeviceGlyphTable> _tableCache;
|
|
private Dictionary<InputDeviceWatcher.InputDeviceCategory, Dictionary<string, Sprite>> _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, 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<string, Sprite> 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<string, DeviceGlyphTable>(StringComparer.OrdinalIgnoreCase);
|
|
_tableCache.Clear();
|
|
|
|
_pathLookup ??= new Dictionary<InputDeviceWatcher.InputDeviceCategory, Dictionary<string, Sprite>>();
|
|
_pathLookup.Clear();
|
|
InitializeLookup(InputDeviceWatcher.InputDeviceCategory.Keyboard);
|
|
InitializeLookup(InputDeviceWatcher.InputDeviceCategory.Xbox);
|
|
InitializeLookup(InputDeviceWatcher.InputDeviceCategory.PlayStation);
|
|
InitializeLookup(InputDeviceWatcher.InputDeviceCategory.Other);
|
|
|
|
if (tables == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < tables.Count; i++)
|
|
{
|
|
DeviceGlyphTable table = tables[i];
|
|
if (table == null || string.IsNullOrWhiteSpace(table.deviceName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_tableCache[table.deviceName] = table;
|
|
InputDeviceWatcher.InputDeviceCategory category = ParseCategory(table.deviceName);
|
|
Dictionary<string, Sprite> map = _pathLookup[category];
|
|
RegisterEntries(table, map);
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
public void EditorRefreshCache()
|
|
{
|
|
BuildCache();
|
|
}
|
|
|
|
public static string EditorNormalizeControlPath(string controlPath)
|
|
{
|
|
return NormalizeControlPath(controlPath);
|
|
}
|
|
#endif
|
|
|
|
private void InitializeLookup(InputDeviceWatcher.InputDeviceCategory category)
|
|
{
|
|
_pathLookup[category] = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private void RegisterEntries(DeviceGlyphTable table, Dictionary<string, Sprite> 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<string, Sprite> 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)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(controlPath))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
if (NormalizedPathCache.TryGetValue(controlPath, out string normalizedPath))
|
|
{
|
|
return normalizedPath;
|
|
}
|
|
|
|
normalizedPath = CanonicalizeDeviceLayout(controlPath.Trim().ToLowerInvariant());
|
|
NormalizedPathCache[controlPath] = normalizedPath;
|
|
return normalizedPath;
|
|
}
|
|
|
|
private static string CanonicalizeDeviceLayout(string controlPath)
|
|
{
|
|
int start = controlPath.IndexOf('<');
|
|
int end = controlPath.IndexOf('>');
|
|
if (start < 0 || end <= start + 1)
|
|
{
|
|
return controlPath;
|
|
}
|
|
|
|
string layout = controlPath.Substring(start + 1, end - start - 1);
|
|
string canonicalLayout = GetCanonicalLayout(layout);
|
|
if (string.Equals(layout, canonicalLayout, StringComparison.Ordinal))
|
|
{
|
|
return controlPath;
|
|
}
|
|
|
|
return controlPath.Substring(0, start + 1) + canonicalLayout + controlPath.Substring(end);
|
|
}
|
|
|
|
private static string GetCanonicalLayout(string layout)
|
|
{
|
|
if (string.IsNullOrEmpty(layout))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
if (layout.IndexOf("keyboard", StringComparison.OrdinalIgnoreCase) >= 0)
|
|
{
|
|
return "keyboard";
|
|
}
|
|
|
|
if (layout.IndexOf("mouse", StringComparison.OrdinalIgnoreCase) >= 0)
|
|
{
|
|
return "mouse";
|
|
}
|
|
|
|
if (layout.IndexOf("joystick", StringComparison.OrdinalIgnoreCase) >= 0)
|
|
{
|
|
return "joystick";
|
|
}
|
|
|
|
if (layout.IndexOf("gamepad", StringComparison.OrdinalIgnoreCase) >= 0
|
|
|| layout.IndexOf("controller", StringComparison.OrdinalIgnoreCase) >= 0
|
|
|| layout.IndexOf("xinput", StringComparison.OrdinalIgnoreCase) >= 0
|
|
|| layout.IndexOf("dualshock", StringComparison.OrdinalIgnoreCase) >= 0
|
|
|| layout.IndexOf("dualsense", StringComparison.OrdinalIgnoreCase) >= 0)
|
|
{
|
|
return "gamepad";
|
|
}
|
|
|
|
return layout;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|