Files
zeling_v2/Assets/_Game/Scripts/UI/HUD/BossHPBar.cs
Joywayer b7baf7ad6a Add WeaponFeedback component and AddressableManagerWindow meta file
- Implemented WeaponFeedback class for handling weapon-related feedbacks such as hit effects and attack sounds.
- Added meta file for AddressableManagerWindow to manage addressable assets.
- Included a new jump.data file for profiler data.
2026-05-22 22:03:32 +08:00

115 lines
4.4 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;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.Core.Events;
namespace BaseGames.UI
{
/// <summary>
/// Boss 血条 UI架构 10_UIModule §4
/// 默认隐藏Boss 战开始时从屏幕底部滑入。
/// 阶段标记点由 BossHPMax 与 PhaseThreshold 共同决定(此处由外部广播 EVT_BossHPMaxSet 设置)。
/// </summary>
public class BossHPBar : MonoBehaviour
{
[SerializeField] private TMP_Text _bossNameText;
[SerializeField] private Image _hpFill;
[SerializeField] private Transform _phaseMarkersRoot;
[SerializeField] private GameObject _phaseMarkerPrefab;
[SerializeField] private float _slideDistance = 120f; // 滑入距离(像素)
[SerializeField] private float _slideDuration = 0.3f;
[Header("Event Channels")]
[SerializeField] private BoolEventChannelSO _onBossFightToggled; // true=开始, false=结束
[SerializeField] private IntEventChannelSO _onBossHPChanged;
[SerializeField] private StringEventChannelSO _onBossNameSet;
[SerializeField] private IntEventChannelSO _onBossHPMaxSet;
[SerializeField] private FloatEventChannelSO _onBossPhaseThreshold; // 每个阶段切换阈值0-1
private int _maxHP;
private RectTransform _rect;
private Vector2 _shownPos;
private Vector2 _hiddenPos;
private Coroutine _slideCoroutine;
private readonly CompositeDisposable _subs = new();
private void Awake()
{
_rect = (RectTransform)transform;
_shownPos = _rect.anchoredPosition;
_hiddenPos = _shownPos - new Vector2(0, _slideDistance);
_rect.anchoredPosition = _hiddenPos;
gameObject.SetActive(false);
}
private void OnEnable()
{
_onBossFightToggled?.Subscribe(OnBossFightToggled).AddTo(_subs);
_onBossHPChanged?.Subscribe(OnHPChanged).AddTo(_subs);
_onBossNameSet?.Subscribe(OnNameSet).AddTo(_subs);
_onBossHPMaxSet?.Subscribe(OnMaxSet).AddTo(_subs);
}
private void OnDisable() => _subs.Clear();
// ── 事件回调 ──────────────────────────────────────────────────────────
private void OnBossFightToggled(bool started)
{
if (_slideCoroutine != null) StopCoroutine(_slideCoroutine);
if (started)
{
gameObject.SetActive(true);
_slideCoroutine = StartCoroutine(SlideTo(_shownPos));
}
else
{
_slideCoroutine = StartCoroutine(SlideOut());
}
}
private void OnHPChanged(int hp)
{
if (_maxHP > 0) _hpFill.fillAmount = (float)hp / _maxHP;
}
private void OnNameSet(string bossName)
{
if (_bossNameText != null) _bossNameText.text = bossName;
}
private void OnMaxSet(int max)
{
_maxHP = max;
// 重建阶段标记(每次 BossHPMax 改变时清空并按已存阈值重建,此处简化为清空)
if (_phaseMarkersRoot != null)
{
// 逆序删除:避免正序枚举 Transform 子节点同时销毁时的迭代器失效
for (int i = _phaseMarkersRoot.childCount - 1; i >= 0; i--)
Destroy(_phaseMarkersRoot.GetChild(i).gameObject);
}
}
// ── 动画协程 ──────────────────────────────────────────────────────────
private IEnumerator SlideTo(Vector2 target)
{
Vector2 start = _rect.anchoredPosition;
float t = 0;
while (t < _slideDuration)
{
_rect.anchoredPosition = Vector2.Lerp(start, target, t / _slideDuration);
t += Time.unscaledDeltaTime;
yield return null;
}
_rect.anchoredPosition = target;
}
private IEnumerator SlideOut()
{
yield return SlideTo(_hiddenPos);
gameObject.SetActive(false);
}
}
}