using System.Collections; using UnityEngine; using UnityEngine.UI; using TMPro; using BaseGames.Core.Events; namespace BaseGames.UI { /// /// Boss 血条 UI(架构 10_UIModule §4)。 /// 默认隐藏,Boss 战开始时从屏幕底部滑入。 /// 阶段标记点由 BossHPMax 与 PhaseThreshold 共同决定(此处由外部广播 EVT_BossHPMaxSet 设置)。 /// 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) foreach (Transform t in _phaseMarkersRoot) Destroy(t.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); } } }