Files
zeling_v2/Assets/_Game/Scripts/UI/ToolHUD.cs
2026-05-25 11:54:37 +08:00

107 lines
3.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Equipment;
namespace BaseGames.UI
{
/// <summary>
/// 工具栏 HUD架构 09_ProgressionModule §7.5)。
/// 显示 2 个工具槽的图标 + 剩余次数 + 冷却遮罩。
/// </summary>
public class ToolHUD : MonoBehaviour
{
[SerializeField] private ToolSlotUI[] _slots; // 2 个 ToolSlotUI 组件
[SerializeField] private ToolUsedEventChannelSO _onToolUsed;
// 通过 ServiceLocator 解析:避免 UI 直接持有 ToolSlotManager 具体类型引用
private IToolSlotService _slotManager;
private readonly CompositeDisposable _subs = new();
// 每个槽独立的冷却追踪协程;仅在冷却期间运行,避免每帧全槽轮询
private Coroutine[] _cooldownCoroutines;
private void Awake()
{
_cooldownCoroutines = _slots != null
? new Coroutine[_slots.Length]
: System.Array.Empty<Coroutine>();
}
private void OnEnable()
{
_slotManager = ServiceLocator.GetOrDefault<IToolSlotService>();
_onToolUsed?.Subscribe(RefreshSlot).AddTo(_subs);
}
private void OnDisable()
{
_subs.Clear();
StopAllCoroutines();
if (_cooldownCoroutines != null)
for (int i = 0; i < _cooldownCoroutines.Length; i++)
_cooldownCoroutines[i] = null;
}
private void RefreshSlot(ToolUsedPayload payload)
{
int i = payload.SlotIndex;
if (_slots == null || i < 0 || i >= _slots.Length) return;
if (_slotManager == null) return; // ToolSlotManager 尚未注册
_slots[i].Refresh(
_slotManager.GetTool(i),
_slotManager.GetRemainingUses(i),
_slotManager.GetCooldownRatio(i));
// 启动(或重启)该槽的冷却追踪协程
if (_cooldownCoroutines[i] != null) StopCoroutine(_cooldownCoroutines[i]);
_cooldownCoroutines[i] = StartCoroutine(TrackCooldown(i));
}
/// <summary>
/// 仅在该槽冷却未结束时每帧更新填充值;冷却归零后自动停止,不消耗额外 CPU。
/// </summary>
private System.Collections.IEnumerator TrackCooldown(int slotIndex)
{
while (_slotManager != null && _slotManager.GetCooldownRatio(slotIndex) > 0f)
{
_slots[slotIndex].SetCooldownFill(_slotManager.GetCooldownRatio(slotIndex));
yield return null;
}
// 冷却结束,确保填充归零
if (slotIndex < _slots.Length)
_slots[slotIndex].SetCooldownFill(0f);
_cooldownCoroutines[slotIndex] = null;
}
}
/// <summary>
/// 单个工具槽 UI图标 + 剩余次数文本 + 冷却遮罩 ImageFillAmount
/// </summary>
public class ToolSlotUI : MonoBehaviour
{
[SerializeField] private Image _icon;
[SerializeField] private TMP_Text _usesText;
[SerializeField] private Image _cooldownMask; // type = Filled, Image Type = Filled
public void Refresh(BaseGames.Equipment.ToolSO tool, int remainingUses, float cooldownRatio)
{
if (_icon != null)
_icon.sprite = tool != null ? tool.icon : null;
if (_usesText != null)
_usesText.text = remainingUses < 0 ? "∞" : remainingUses.ToString();
SetCooldownFill(cooldownRatio);
}
public void SetCooldownFill(float ratio)
{
if (_cooldownMask != null)
_cooldownMask.fillAmount = Mathf.Clamp01(ratio);
}
}
}