using TMPro; using UnityEngine; using UnityEngine.UI; using BaseGames.Core; using BaseGames.Core.Events; namespace BaseGames.UI.HUD { /// /// 交互提示 Widget。 /// /// 职责: /// • 订阅 InteractPromptEventChannelSO 显示/隐藏提示 /// • 显示按键图标(Image)+ 动作文本(TMP_Text) /// • 监听 IInputIconService.OnIconSetChanged,在设备切换或改键后自动刷新图标 /// /// 布置方式:放在 HUD Canvas 下,引用对应的事件频道 SO 资产。 /// 不依赖 HUDController,可独立使用。 /// public sealed class InteractPromptWidget : MonoBehaviour { [Header("UI 引用")] [SerializeField] private Image _keyIcon; [SerializeField] private TMP_Text _labelText; [Tooltip("整个提示根节点,控制显示/隐藏")] [SerializeField] private GameObject _root; [Header("Event Channels")] [SerializeField] private InteractPromptEventChannelSO _onShowPrompt; [SerializeField] private VoidEventChannelSO _onHidePrompt; // ── 运行时状态 ──────────────────────────────────────────────────────── private IInputIconService _iconService; private string _currentActionName; private readonly CompositeDisposable _subs = new(); // ── Lifecycle ───────────────────────────────────────────────────────── private void OnEnable() { // ServiceLocator 可能在此组件 OnEnable 时尚未注册(执行顺序问题), // 延迟到 ShowPrompt 首次调用时再获取,确保服务可用 _onShowPrompt?.Subscribe(ShowPrompt).AddTo(_subs); _onHidePrompt?.Subscribe(HidePrompt).AddTo(_subs); HidePrompt(); } private void OnDisable() { _subs.Clear(); UnsubscribeFromIconService(); } // ── Handlers ────────────────────────────────────────────────────────── private void ShowPrompt(InteractPromptEvent evt) { _currentActionName = evt.ActionName; // 延迟绑定:首次显示时获取服务(确保 ServiceLocator 已初始化) if (_iconService == null) { _iconService = ServiceLocator.GetOrDefault(); if (_iconService != null) _iconService.OnIconSetChanged += RefreshIcon; } if (_labelText != null) _labelText.text = evt.LabelText; RefreshIcon(); if (_root != null) _root.SetActive(true); else gameObject.SetActive(true); } private void HidePrompt() { _currentActionName = null; if (_root != null) _root.SetActive(false); else gameObject.SetActive(false); } // ── Icon Refresh ────────────────────────────────────────────────────── /// 设备切换或改键后刷新图标。由 IInputIconService.OnIconSetChanged 调用。 private void RefreshIcon() { if (_keyIcon == null || string.IsNullOrEmpty(_currentActionName)) return; var sprite = _iconService?.GetActionIcon(_currentActionName); if (sprite != null) { _keyIcon.sprite = sprite; _keyIcon.enabled = true; } else { // 找不到图标时隐藏图标格,避免显示错误占位图 _keyIcon.enabled = false; } } private void UnsubscribeFromIconService() { if (_iconService != null) { _iconService.OnIconSetChanged -= RefreshIcon; _iconService = null; } } } }