Files
zeling_v2/Assets/_Game/Scripts/UI/HUD/HUDController.cs
Joywayer a1f54b68e6 Refactor interaction prompt system to use world space prompts
- Removed the InteractPromptWidget from HUD and its references in HUDController.
- Introduced IInteractPromptView interface for world space interaction prompts.
- Implemented WorldInteractPrompt class to manage display of interaction prompts in world space.
- Updated InteractableDetector to handle showing/hiding of world space prompts based on player proximity to interactable objects.
- Created a new prefab for UI_WorldInteractPrompt to facilitate the new interaction prompt system.
2026-06-10 14:14:08 +08:00

132 lines
5.5 KiB
C#
Raw 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 System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.Core.Events;
namespace BaseGames.UI.HUD
{
/// <summary>
/// 游戏内 HUD 控制器(架构 10_UIModule §2
/// 订阅事件频道更新 HP 格子、魂魂力/灵力/灵珠/弹簧充能和形态图标。
/// HP/弹簧格子采用 SetActive 复用,避免运行期频繁 Instantiate/Destroy。
/// </summary>
public class HUDController : MonoBehaviour
{
[Header("HP")]
[SerializeField] private Transform _hpContainer;
[SerializeField] private GameObject _hpCellPrefab;
[Header("Gauges")]
[SerializeField] private Image _soulGaugeFill;
[SerializeField] private Image _spiritGaugeFill;
[SerializeField] private TMP_Text _lingZhuText;
[Header("Spring Charges")]
[SerializeField] private Transform _springContainer;
[SerializeField] private GameObject _springIconPrefab;
[Header("Form")]
[SerializeField] private Image[] _formIcons;
// 交互提示已改为「每个交互物自带的世界空间提示」WorldInteractPrompt不再由 HUD 承载。
[Header("Event Channels - Subscribe")]
[SerializeField] private IntEventChannelSO _onHPChanged;
[SerializeField] private IntEventChannelSO _onMaxHPChanged;
[SerializeField] private IntEventChannelSO _onSoulPowerChanged;
[SerializeField] private IntEventChannelSO _onSpiritPowerChanged;
[SerializeField] private IntEventChannelSO _onLingZhuChanged;
[SerializeField] private IntEventChannelSO _onSpringChargesChanged;
[SerializeField] private IntEventChannelSO _onFormChanged;
private readonly List<GameObject> _hpCells = new();
private readonly List<GameObject> _springIcons = new();
private readonly CompositeDisposable _subs = new();
private int _lastLingZhu = int.MinValue;
// ── 性能基线 (Profiler 窗口可在 "Scripts" 类别下看到) ──
private static readonly ProfilerMarker s_RebuildHPMarker = new("HUD.RebuildHPCells");
private static readonly ProfilerMarker s_RebuildSpringMarker = new("HUD.RebuildSpringIcons");
private void OnEnable()
{
// MaxHP 必须先于 HP 订阅:粘性回放时先 RebuildHPCells 建满格子,再 UpdateHP 设激活数,
// 否则受伤档CurrentHP<MaxHP回放顺序颠倒会导致 UpdateHP 作用于空列表后被全量重建覆盖。
_onMaxHPChanged?.Subscribe(RebuildHPCells).AddTo(_subs);
_onHPChanged?.Subscribe(UpdateHP).AddTo(_subs);
_onSoulPowerChanged?.Subscribe(UpdateSoul).AddTo(_subs);
_onSpiritPowerChanged?.Subscribe(UpdateSpirit).AddTo(_subs);
_onLingZhuChanged?.Subscribe(UpdateLingZhu).AddTo(_subs);
_onSpringChargesChanged?.Subscribe(RebuildSpringIcons).AddTo(_subs);
_onFormChanged?.Subscribe(UpdateFormIcon).AddTo(_subs);
// 交互提示由独立的 InteractPromptWidget 组件处理HUDController 不再直接订阅
}
private void OnDisable() => _subs.Clear();
private void UpdateHP(int current)
{
for (int i = 0; i < _hpCells.Count; i++)
if (_hpCells[i] != null) _hpCells[i].SetActive(i < current);
}
private void RebuildHPCells(int max)
{
using var _ = s_RebuildHPMarker.Auto();
if (_hpContainer == null || _hpCellPrefab == null) return;
// 复用现有 Cell仅在数量不足时 Instantiate 补充,超出时 SetActive(false) 而非 Destroy
for (int i = 0; i < max; i++)
{
if (i < _hpCells.Count)
_hpCells[i].SetActive(true);
else
_hpCells.Add(Instantiate(_hpCellPrefab, _hpContainer));
}
for (int i = max; i < _hpCells.Count; i++)
if (_hpCells[i] != null) _hpCells[i].SetActive(false);
}
private void UpdateSoul(int val)
{
if (_soulGaugeFill != null) _soulGaugeFill.fillAmount = val / 100f;
}
private void UpdateSpirit(int val)
{
if (_spiritGaugeFill != null) _spiritGaugeFill.fillAmount = val / 100f;
}
private void UpdateLingZhu(int val)
{
if (_lingZhuText == null || val == _lastLingZhu) return;
_lastLingZhu = val;
_lingZhuText.text = val.ToString();
}
private void RebuildSpringIcons(int charges)
{
using var _ = s_RebuildSpringMarker.Auto();
if (_springContainer == null || _springIconPrefab == null) return;
// 复用已有图标,超出数量时 SetActive(false)
for (int i = 0; i < charges; i++)
{
if (i < _springIcons.Count)
_springIcons[i].SetActive(true);
else
_springIcons.Add(Instantiate(_springIconPrefab, _springContainer));
}
for (int i = charges; i < _springIcons.Count; i++)
if (_springIcons[i] != null) _springIcons[i].SetActive(false);
}
private void UpdateFormIcon(int formIndex)
{
if (_formIcons == null) return;
for (int i = 0; i < _formIcons.Length; i++)
if (_formIcons[i] != null) _formIcons[i].enabled = (i == formIndex);
}
}
}