- Created a new markdown file detailing the configuration of InputDeviceIconSetSO. - Included sections on system architecture, field explanations, image specifications, and complete workflow from setup to runtime. - Documented the automatic device recognition logic and provided troubleshooting for common issues. - Added references to relevant files and scripts for easier navigation.
120 lines
4.4 KiB
C#
120 lines
4.4 KiB
C#
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Events;
|
||
|
||
namespace BaseGames.UI.HUD
|
||
{
|
||
/// <summary>
|
||
/// 交互提示 Widget。
|
||
///
|
||
/// 职责:
|
||
/// • 订阅 InteractPromptEventChannelSO 显示/隐藏提示
|
||
/// • 显示按键图标(Image)+ 动作文本(TMP_Text)
|
||
/// • 监听 IInputIconService.OnIconSetChanged,在设备切换或改键后自动刷新图标
|
||
///
|
||
/// 布置方式:放在 HUD Canvas 下,引用对应的事件频道 SO 资产。
|
||
/// 不依赖 HUDController,可独立使用。
|
||
/// </summary>
|
||
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<IInputIconService>();
|
||
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 ──────────────────────────────────────────────────────
|
||
|
||
/// <summary>设备切换或改键后刷新图标。由 IInputIconService.OnIconSetChanged 调用。</summary>
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|