using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using BaseGames.Core; namespace BaseGames.UI { /// /// 单个按键图标 Image 组件。 /// /// 支持两种查询模式: /// • ByActionName(推荐):填写 ActionName(如 "Interact"), /// 由 IInputIconService 自动解析当前设备 + 改键后的实际绑定路径 → 图标。 /// • ByBindingPath(兼容/装饰用):直接填写固定路径(如 "<Keyboard>/space"), /// 适合教程截图等不跟随改键变化的场景。 /// /// ⚠️ 必须独立成文件(类名 = 文件名):作为可序列化的 MonoBehaviour, /// 若与其他类同处一个 .cs 文件(二级类),Unity 无法为其写出有效的 m_Script 引用, /// 挂载后存盘 / 重载会变成"缺失脚本"。 /// [RequireComponent(typeof(Image))] public class InputIconImage : MonoBehaviour { public enum LookupMode { ByActionName, ByBindingPath } [SerializeField] private LookupMode _mode = LookupMode.ByActionName; [Tooltip("Action 名称,如 Interact / Jump / Attack(仅 ByActionName 模式使用)")] [SerializeField] private string _actionName; [Tooltip("固定绑定路径,如 /space(仅 ByBindingPath 模式使用)")] [SerializeField] private string _bindingPath; private Image _image; private IInputIconService _iconService; // ── 静态注册表:替换 FindObjectsByType,O(1) 注册/注销,O(n) 广播 ──────── private static readonly List _registry = new(); /// 通知注册表内所有已启用实例刷新图标(设备切换时调用)。 internal static void RefreshAll() { for (int i = _registry.Count - 1; i >= 0; i--) { if (_registry[i] != null) _registry[i].Refresh(); else _registry.RemoveAt(i); // 清理已销毁的残留引用 } } private void Awake() => _image = GetComponent(); private void OnEnable() { _iconService = ServiceLocator.GetOrDefault(); if (_iconService != null) _iconService.OnIconSetChanged += Refresh; _registry.Add(this); Refresh(); } private void OnDisable() { if (_iconService != null) _iconService.OnIconSetChanged -= Refresh; _registry.Remove(this); } /// 刷新图标显示。设备切换或改键后由 InputDeviceIconSwitcher / InputIconService 调用。 public void Refresh() { if (_image == null) return; // 若组件在 IInputIconService 注册前 Enable,此处补重试并补订阅 if (_iconService == null) { _iconService = ServiceLocator.GetOrDefault(); if (_iconService != null) _iconService.OnIconSetChanged += Refresh; } Sprite sprite = null; if (_mode == LookupMode.ByActionName && !string.IsNullOrEmpty(_actionName)) { sprite = _iconService?.GetActionIcon(_actionName); } else if (_mode == LookupMode.ByBindingPath && !string.IsNullOrEmpty(_bindingPath)) { // 使用固定路径在当前图标集查找(不随改键变化),适合装饰性按键说明 sprite = _iconService?.GetPathIcon(_bindingPath); } if (sprite != null) { _image.sprite = sprite; _image.enabled = true; } else { _image.enabled = false; } } } }