using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; namespace SLSUtilities.UI { /// /// 从 读取当前按键绑定, /// 将 Input Action 名映射为 InputGlyphs SpriteAsset 中的 glyph token 名。 /// /// 初始化后, 自动使用本解析器, /// 使得本地化文本可以使用 Action 名作为 Token(如 [Interact]), /// 无需硬编码具体按键名(如 [R])。 /// 当玩家重新绑定按键时,重新调用 即可自动更新图标。 /// /// public static class InputBindingResolver { private static readonly Dictionary ActionGlyphMap = new(); private static bool isInitialized; /// 是否已初始化。 public static bool IsInitialized => isInitialized; // ================================================================ // Control Path → Glyph Token 特殊映射 // ================================================================ /// /// 无法通过通用规则推导的控制路径,手动映射到 glyph token。 /// private static readonly Dictionary ControlPathOverrides = new() { // 鼠标 { "/leftButton", "LMB" }, { "/rightButton", "RMB" }, { "/middleButton", "MMB" }, { "/forwardButton", "Mouse4" }, { "/backButton", "Mouse5" }, // 修饰键(左右合并为同一图标) { "/leftCtrl", "Ctrl" }, { "/rightCtrl", "Ctrl" }, { "/leftShift", "Shift" }, { "/rightShift", "Shift" }, { "/leftAlt", "Alt" }, { "/rightAlt", "Alt" }, // 名称差异较大的按键 { "/escape", "Esc" }, { "/delete", "Del" }, { "/insert", "Ins" }, { "/pageUp", "Pgup" }, { "/pageDown", "Pgdn" }, { "/printScreen","Prtsc" }, { "/scrollLock", "Scrlk" }, { "/capsLock", "Caps" }, { "/numpadEnter","Enter" }, { "/contextMenu","Context" }, { "/leftMeta", "Windows" }, { "/rightMeta", "Windows" }, { "/backquote", "Tilde" }, { "/minus", "Hyphen" }, { "/equals", "Equals" }, { "/leftBracket","BracketOpen" }, { "/rightBracket","BracketClose" }, { "/backslash", "BackwardSlash" }, { "/semicolon", "SemiColon" }, { "/quote", "Quote" }, { "/comma", "Comma" }, { "/period", "Dot" }, { "/slash", "ForwardSlash" }, { "/numpadPlus", "Plus" }, // 箭头键 { "/upArrow", "ArrowUp" }, { "/downArrow", "ArrowDown" }, { "/leftArrow", "ArrowLeft" }, { "/rightArrow", "ArrowRight" }, }; // ================================================================ // 初始化 // ================================================================ /// /// 从 InputActionAsset 构建 Action 名 → Glyph Token 的映射表。 /// 仅处理指定 controlScheme 的非 composite 绑定。 /// /// 包含所有 ActionMap 的 InputActionAsset。 /// /// 要匹配的 Control Scheme 名称(如 "KeyboardMouse")。 /// 为 null 时取第一个匹配的绑定。 /// 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; } /// /// 将 Token 解析为 glyph token 名。 /// 如果 token 匹配已注册的 Action 名,则返回对应按键的 glyph token; /// 否则原样返回(可能是直接按键名,如 "LMB"、"Q")。 /// public static string ResolveToken(string token) { if (isInitialized && ActionGlyphMap.TryGetValue(token, out string glyph)) { return glyph; } return token; } /// /// 获取指定 Action 名当前绑定的 glyph token。 /// 未找到时返回 null。 /// public static string GetGlyphForAction(string actionName) { if (isInitialized && ActionGlyphMap.TryGetValue(actionName, out string glyph)) { return glyph; } return null; } // ================================================================ // 内部实现 // ================================================================ /// /// 取 Action 的第一个匹配 controlScheme 的非 composite 绑定, /// 解析其控制路径为 glyph token。 /// 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; } /// /// 检查绑定是否属于指定的 Control Scheme。 /// controlScheme 为 null 时始终匹配。 /// 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); } /// /// 将 Input System 控制路径转换为 InputGlyphs SpriteAsset 中的 token 名。 /// private static string ControlPathToGlyphToken(string controlPath) { if (string.IsNullOrEmpty(controlPath)) return null; // 优先查找特殊映射 if (ControlPathOverrides.TryGetValue(controlPath, out string token)) return token; // 通用键盘规则 const string keyboardPrefix = "/"; if (controlPath.StartsWith(keyboardPrefix)) { string key = controlPath.Substring(keyboardPrefix.Length); return KeyNameToGlyphToken(key); } return null; } /// /// 将 Input System 的键盘 key 名转换为 glyph token 名。 /// 规则与 InputGlyphsSpriteAssetCreator.MapKeyboardName 保持一致。 /// 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); } } }