Files
gold_dolphin/unity/Assets/UI/GameHUD.cs
T
2026-07-05 21:53:06 +08:00

248 lines
9.8 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 UnityEngine;
using UnityEngine.UI;
using TMPro;
using IndianOceanAssets.Engine2_5D;
namespace GameFramework
{
/// <summary>
/// 游戏内 HUD —— 魂灵计数 + 生命图标 + 技能CD。
///
/// 布局:
/// - 左上角:收集到的魂灵(icon + 数字)
/// - 左下角:5个生命图标
/// - 右下角:3个技能图标(冲刺/摇铃/灵灯)+ CD遮罩
/// </summary>
public class GameHUD : MonoBehaviour
{
/// <summary>魂灵图标的 RectTransform(供魂灵掉落物飞行定位)</summary>
public static RectTransform SoulIconRect { get; private set; }
[Header("魂灵计数(左上角)")]
[SerializeField] private Image soulIcon;
[SerializeField] private TextMeshProUGUI soulCountText;
[Header("生命图标(左下角)")]
[SerializeField] private Image[] lifeIcons; // 5个生命图标
[Header("技能图标(右下角)")]
[SerializeField] private Image skillSprintIcon;
[SerializeField] private Image skillSprintCD; // CD 遮罩(Image.fillAmount
[SerializeField] private Image skillBellIcon;
[SerializeField] private Image skillBellCD;
[SerializeField] private Image skillLanternIcon;
[SerializeField] private Image skillLanternCD;
[Header("暂停")]
[SerializeField] private Button pauseButton;
private HealthSystem _playerHealth;
private SpiritLanternSystem _lanternSystem;
private EchoSystem _echoSystem;
private PlayerController _playerController;
private int _maxHealth = 5;
private bool _subscribedScoreEvent = false;
private int _lastSoulCount = -1;
void Start()
{
var player = GameObject.FindWithTag("Player");
if (player != null)
{
_playerHealth = player.GetComponent<HealthSystem>();
_lanternSystem = player.GetComponent<SpiritLanternSystem>();
_echoSystem = player.GetComponent<EchoSystem>();
_playerController = player.GetComponent<PlayerController>();
}
if (pauseButton != null)
pauseButton.onClick.AddListener(OnPauseClick);
// 暴露魂灵图标 RectTransform(供魂灵掉落物飞行定位)
if (soulIcon != null)
SoulIconRect = soulIcon.rectTransform;
// 订阅分数事件(魂灵计数复用分数系统)
if (ScoreManager.Instance != null)
{
ScoreManager.onScoreChanged += UpdateSoulCount;
_subscribedScoreEvent = true;
UpdateSoulCount(ScoreManager.Instance.CurrentScore);
}
UpdateLifeIcons();
// 自动创建受击泛红特效(如果场景中没有)
if (FindObjectOfType<DamageFlashOverlay>() == null)
{
var overlayObj = new GameObject("DamageFlashOverlay");
overlayObj.AddComponent<DamageFlashOverlay>();
}
}
void OnDestroy()
{
if (_subscribedScoreEvent)
{
ScoreManager.onScoreChanged -= UpdateSoulCount;
_subscribedScoreEvent = false;
}
}
void Update()
{
// 延迟订阅 ScoreManager(跨场景后 ScoreManager 可能才创建)
if (!_subscribedScoreEvent && ScoreManager.Instance != null)
{
ScoreManager.onScoreChanged += UpdateSoulCount;
_subscribedScoreEvent = true;
UpdateSoulCount(ScoreManager.Instance.CurrentScore);
Debug.Log("[GameHUD] 延迟订阅 ScoreManager.onScoreChanged 成功");
}
// 延迟查找玩家(GameSpawnManager 可能比 HUD 晚创建玩家)
if (_playerHealth == null)
{
var player = GameObject.FindWithTag("Player");
if (player != null)
{
_playerHealth = player.GetComponent<HealthSystem>();
_lanternSystem = player.GetComponent<SpiritLanternSystem>();
_echoSystem = player.GetComponent<EchoSystem>();
_playerController = player.GetComponent<PlayerController>();
Debug.Log("[GameHUD] 延迟找到玩家,已绑定组件");
}
}
UpdateLifeIcons();
UpdateSkillCooldowns();
}
/// <summary>
/// 更新魂灵计数(左上角)。
/// </summary>
private void UpdateSoulCount(int count)
{
if (soulCountText != null)
{
soulCountText.text = count.ToString();
if (count != _lastSoulCount)
{
Debug.Log($"[GameHUD] UpdateSoulCount: {count}");
_lastSoulCount = count;
}
}
else
{
Debug.LogWarning("[GameHUD] soulCountText 未赋值!");
}
}
/// <summary>
/// 更新生命图标(左下角)。
/// 根据当前血量显示/隐藏对应图标。
/// </summary>
private void UpdateLifeIcons()
{
if (_playerHealth == null || lifeIcons == null || lifeIcons.Length == 0) return;
// 获取当前血量
var healthField = typeof(HealthSystem).GetField("health",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
int currentHealth = healthField != null ? (int)healthField.GetValue(_playerHealth) : _maxHealth;
for (int i = 0; i < lifeIcons.Length; i++)
{
if (lifeIcons[i] != null)
lifeIcons[i].enabled = (i < currentHealth);
}
}
/// <summary>
/// 更新技能CD遮罩(右下角)。
/// 使用 Image.fillAmount 实现圆形CD效果。
/// </summary>
private void UpdateSkillCooldowns()
{
// 冲刺 CD(从 PlayerController 读取 rollCooldown + lastRollTime
if (_playerController != null)
{
var cdField = typeof(PlayerController).GetField("rollCooldown",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var lastField = typeof(PlayerController).GetField("lastRollTime",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
float sprintCD = cdField != null ? (float)cdField.GetValue(_playerController) : 1f;
float lastRoll = lastField != null ? (float)lastField.GetValue(_playerController) : -999f;
float remaining = Mathf.Max(0f, (lastRoll + sprintCD) - Time.time);
UpdateSkillCD(skillSprintCD, skillSprintIcon, remaining, sprintCD);
}
// 摇铃 CD(从 EchoSystem 读取 cooldown + _lastEchoTime
if (_echoSystem != null)
{
var cdField = typeof(EchoSystem).GetField("cooldown",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var lastField = typeof(EchoSystem).GetField("_lastEchoTime",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
float bellCD = cdField != null ? (float)cdField.GetValue(_echoSystem) : 2f;
float lastTime = lastField != null ? (float)lastField.GetValue(_echoSystem) : -999f;
float remaining = Mathf.Max(0f, (lastTime + bellCD) - Time.time);
UpdateSkillCD(skillBellCD, skillBellIcon, remaining, bellCD);
}
// 灵灯 CD(从 SpiritLanternSystem 读取)
if (_lanternSystem != null)
{
float remaining = _lanternSystem.CooldownRemaining;
var cdField = typeof(SpiritLanternSystem).GetField("cooldown",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
float maxCD = cdField != null ? (float)cdField.GetValue(_lanternSystem) : 3f;
UpdateSkillCD(skillLanternCD, skillLanternIcon, remaining, maxCD);
}
}
/// <summary>
/// 更新单个技能CD遮罩。
/// fillAmount = 1 表示CD中(完全遮挡),0 表示就绪(无遮挡)。
/// CD 时图标变灰,就绪时恢复。
/// </summary>
private void UpdateSkillCD(Image cdImage, float remaining, float total)
{
UpdateSkillCD(cdImage, null, remaining, total);
}
/// <summary>
/// 更新单个技能CD遮罩 + 图标去色。
/// </summary>
private void UpdateSkillCD(Image cdImage, Image iconImage, float remaining, float total)
{
if (cdImage == null) return;
bool onCooldown = total > 0f && remaining > 0f;
if (onCooldown)
{
cdImage.gameObject.SetActive(true);
cdImage.fillAmount = remaining / total;
// CD 中图标变灰
if (iconImage != null)
iconImage.color = new Color(0.4f, 0.4f, 0.4f, 1f);
}
else
{
cdImage.fillAmount = 0f;
cdImage.gameObject.SetActive(false);
// CD 就绪图标恢复原色
if (iconImage != null)
iconImage.color = Color.white;
}
}
private void OnPauseClick()
{
if (TimeController.Instance != null)
TimeController.Instance.Pause();
}
}
}