AlicizaX/Client/Assets/Scripts/CustomeModule/InputGlyph/GlyphService.cs

205 lines
8.2 KiB
C#
Raw Normal View History

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