Passion & UI

This commit is contained in:
SoulliesOfficial
2026-06-12 17:11:39 -04:00
parent 7bc1e1722c
commit 6d7ebc5825
3444 changed files with 865284 additions and 463132 deletions

View File

@@ -0,0 +1,249 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
namespace SLSUtilities.UI
{
/// <summary>
/// 从 <see cref="InputActionAsset"/> 读取当前按键绑定,
/// 将 Input Action 名映射为 InputGlyphs SpriteAsset 中的 glyph token 名。
/// <para>
/// 初始化后,<see cref="InputGlyphParser"/> 自动使用本解析器,
/// 使得本地化文本可以使用 Action 名作为 Token如 <c>[Interact]</c>
/// 无需硬编码具体按键名(如 <c>[R]</c>)。
/// 当玩家重新绑定按键时,重新调用 <see cref="Initialize"/> 即可自动更新图标。
/// </para>
/// </summary>
public static class InputBindingResolver
{
private static readonly Dictionary<string, string> ActionGlyphMap = new();
private static bool isInitialized;
/// <summary>是否已初始化。</summary>
public static bool IsInitialized => isInitialized;
// ================================================================
// Control Path → Glyph Token 特殊映射
// ================================================================
/// <summary>
/// 无法通过通用规则推导的控制路径,手动映射到 glyph token。
/// </summary>
private static readonly Dictionary<string, string> ControlPathOverrides = new()
{
// 鼠标
{ "<Mouse>/leftButton", "LMB" },
{ "<Mouse>/rightButton", "RMB" },
{ "<Mouse>/middleButton", "MMB" },
{ "<Mouse>/forwardButton", "Mouse4" },
{ "<Mouse>/backButton", "Mouse5" },
// 修饰键(左右合并为同一图标)
{ "<Keyboard>/leftCtrl", "Ctrl" },
{ "<Keyboard>/rightCtrl", "Ctrl" },
{ "<Keyboard>/leftShift", "Shift" },
{ "<Keyboard>/rightShift", "Shift" },
{ "<Keyboard>/leftAlt", "Alt" },
{ "<Keyboard>/rightAlt", "Alt" },
// 名称差异较大的按键
{ "<Keyboard>/escape", "Esc" },
{ "<Keyboard>/delete", "Del" },
{ "<Keyboard>/insert", "Ins" },
{ "<Keyboard>/pageUp", "Pgup" },
{ "<Keyboard>/pageDown", "Pgdn" },
{ "<Keyboard>/printScreen","Prtsc" },
{ "<Keyboard>/scrollLock", "Scrlk" },
{ "<Keyboard>/capsLock", "Caps" },
{ "<Keyboard>/numpadEnter","Enter" },
{ "<Keyboard>/contextMenu","Context" },
{ "<Keyboard>/leftMeta", "Windows" },
{ "<Keyboard>/rightMeta", "Windows" },
{ "<Keyboard>/backquote", "Tilde" },
{ "<Keyboard>/minus", "Hyphen" },
{ "<Keyboard>/equals", "Equals" },
{ "<Keyboard>/leftBracket","BracketOpen" },
{ "<Keyboard>/rightBracket","BracketClose" },
{ "<Keyboard>/backslash", "BackwardSlash" },
{ "<Keyboard>/semicolon", "SemiColon" },
{ "<Keyboard>/quote", "Quote" },
{ "<Keyboard>/comma", "Comma" },
{ "<Keyboard>/period", "Dot" },
{ "<Keyboard>/slash", "ForwardSlash" },
{ "<Keyboard>/numpadPlus", "Plus" },
// 箭头键
{ "<Keyboard>/upArrow", "ArrowUp" },
{ "<Keyboard>/downArrow", "ArrowDown" },
{ "<Keyboard>/leftArrow", "ArrowLeft" },
{ "<Keyboard>/rightArrow", "ArrowRight" },
};
// ================================================================
// 初始化
// ================================================================
/// <summary>
/// 从 InputActionAsset 构建 Action 名 → Glyph Token 的映射表。
/// 仅处理指定 controlScheme 的非 composite 绑定。
/// </summary>
/// <param name="inputActions">包含所有 ActionMap 的 InputActionAsset。</param>
/// <param name="controlScheme">
/// 要匹配的 Control Scheme 名称(如 "KeyboardMouse")。
/// 为 null 时取第一个匹配的绑定。
/// </param>
public static void Initialize(InputActionAsset inputActions, string controlScheme = "KeyboardMouse")
{
ActionGlyphMap.Clear();
if (inputActions == null)
{
Debug.LogWarning("[InputBindingResolver] InputActionAsset 为 null无法初始化。");
isInitialized = false;
return;
}
foreach (InputActionMap map in inputActions.actionMaps)
{
foreach (InputAction action in map.actions)
{
string glyph = ResolveFirstBinding(action, controlScheme);
if (glyph != null)
{
ActionGlyphMap[action.name] = glyph;
}
}
}
ActionGlyphMap["M0"] = ActionGlyphMap["MainWeaponPrimary"];
ActionGlyphMap["M1"] = ActionGlyphMap["MainWeaponSecondary"];
ActionGlyphMap["MA"] = ActionGlyphMap["MainWeaponSpecialA"];
ActionGlyphMap["MB"] = ActionGlyphMap["MainWeaponSpecialB"];
ActionGlyphMap["MC"] = ActionGlyphMap["MainWeaponSpecialC"];
ActionGlyphMap["S0"] = ActionGlyphMap["SupportEquipment0"];
ActionGlyphMap["S1"] = ActionGlyphMap["SupportEquipment1"];
ActionGlyphMap["S2"] = ActionGlyphMap["SupportEquipment2"];
ActionGlyphMap["S3"] = ActionGlyphMap["SupportEquipment3"];
isInitialized = true;
}
/// <summary>
/// 将 Token 解析为 glyph token 名。
/// 如果 token 匹配已注册的 Action 名,则返回对应按键的 glyph token
/// 否则原样返回(可能是直接按键名,如 "LMB"、"Q")。
/// </summary>
public static string ResolveToken(string token)
{
if (isInitialized && ActionGlyphMap.TryGetValue(token, out string glyph))
{
return glyph;
}
return token;
}
/// <summary>
/// 获取指定 Action 名当前绑定的 glyph token。
/// 未找到时返回 null。
/// </summary>
public static string GetGlyphForAction(string actionName)
{
if (isInitialized && ActionGlyphMap.TryGetValue(actionName, out string glyph))
{
return glyph;
}
return null;
}
// ================================================================
// 内部实现
// ================================================================
/// <summary>
/// 取 Action 的第一个匹配 controlScheme 的非 composite 绑定,
/// 解析其控制路径为 glyph token。
/// </summary>
private static string ResolveFirstBinding(InputAction action, string controlScheme)
{
foreach (InputBinding binding in action.bindings)
{
if (binding.isComposite || binding.isPartOfComposite)
continue;
if (!IsBindingMatchingScheme(binding, controlScheme))
continue;
string path = binding.effectivePath;
string glyph = ControlPathToGlyphToken(path);
if (glyph != null)
return glyph;
}
return null;
}
/// <summary>
/// 检查绑定是否属于指定的 Control Scheme。
/// controlScheme 为 null 时始终匹配。
/// </summary>
private static bool IsBindingMatchingScheme(InputBinding binding, string controlScheme)
{
if (string.IsNullOrEmpty(controlScheme))
return true;
// binding.groups 格式为 "KeyboardMouse" 或 "KeyboardMouse;Gamepad" 等
if (string.IsNullOrEmpty(binding.groups))
return false;
return binding.groups.Contains(controlScheme);
}
/// <summary>
/// 将 Input System 控制路径转换为 InputGlyphs SpriteAsset 中的 token 名。
/// </summary>
private static string ControlPathToGlyphToken(string controlPath)
{
if (string.IsNullOrEmpty(controlPath))
return null;
// 优先查找特殊映射
if (ControlPathOverrides.TryGetValue(controlPath, out string token))
return token;
// 通用键盘规则
const string keyboardPrefix = "<Keyboard>/";
if (controlPath.StartsWith(keyboardPrefix))
{
string key = controlPath.Substring(keyboardPrefix.Length);
return KeyNameToGlyphToken(key);
}
return null;
}
/// <summary>
/// 将 Input System 的键盘 key 名转换为 glyph token 名。
/// 规则与 <c>InputGlyphsSpriteAssetCreator.MapKeyboardName</c> 保持一致。
/// </summary>
private static string KeyNameToGlyphToken(string key)
{
if (string.IsNullOrEmpty(key))
return null;
// 单字母 → 大写
if (key.Length == 1 && char.IsLetter(key[0]))
return key.ToUpperInvariant();
// 单数字 → 原样
if (key.Length == 1 && char.IsDigit(key[0]))
return key;
// 功能键 f1-f12 → 全大写
if (key.Length >= 2 && key[0] == 'f' && int.TryParse(key.Substring(1), out _))
return key.ToUpperInvariant();
// 其余 → 首字母大写space → Space, tab → Tab, enter → Enter, etc.
return char.ToUpperInvariant(key[0]) + key.Substring(1);
}
}
}