This commit is contained in:
2026-07-07 03:34:56 +08:00
parent e7891d7e00
commit ecb20cf370
81 changed files with 2108 additions and 293 deletions
+37 -75
View File
@@ -2,12 +2,14 @@ using UnityEngine;
using UnityEngine.UI;
using TMPro;
using IndianOceanAssets.Engine2_5D;
using Architecture.Core;
using Architecture.Variables;
namespace GameFramework
{
/// <summary>
/// 游戏内 HUD —— 魂灵计数 + 生命图标 + 技能CD。
///
///
/// 布局:
/// - 左上角:收集到的魂灵(icon + 数字)
/// - 左下角:5个生命图标
@@ -36,12 +38,14 @@ namespace GameFramework
[Header("暂停")]
[SerializeField] private Button pauseButton;
[Header("SO 事件通道 / 变量")]
[SerializeField] private IntEvent scoreChangedEvent;
[SerializeField] private IntVariable playerHealthVar;
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()
@@ -62,59 +66,34 @@ namespace GameFramework
if (soulIcon != null)
SoulIconRect = soulIcon.rectTransform;
// 订阅分数事件(魂灵计数复用分数系统)
if (ScoreManager.Instance != null)
// 订阅分数事件(魂灵计数复用分数系统):无条件注册,事件资产始终存在;
// 初始值若 ScoreManager 已就绪则取实时分,否则先显示 0,首个 ScoreChanged 会刷新。
scoreChangedEvent?.Register(UpdateSoulCount);
UpdateSoulCount(ScoreManager.Instance != null ? ScoreManager.Instance.CurrentScore : 0);
// 订阅玩家血量变量(替代反射读取私有字段 + Update 条件 Find 回退)
if (playerHealthVar != null)
{
ScoreManager.onScoreChanged += UpdateSoulCount;
_subscribedScoreEvent = true;
UpdateSoulCount(ScoreManager.Instance.CurrentScore);
playerHealthVar.OnValueChanged += UpdateLifeIcons;
UpdateLifeIcons(playerHealthVar.Value);
}
UpdateLifeIcons();
// 自动创建受击泛红特效(如果场景中没有)
// 受击泛红特效:场景需预置已接线的 DamageFlashOverlay 实例(不再自动创建,
// 否则会生成一个事件为 null 的实例,既无效果又掩盖「未接线」问题)
if (FindObjectOfType<DamageFlashOverlay>() == null)
{
var overlayObj = new GameObject("DamageFlashOverlay");
overlayObj.AddComponent<DamageFlashOverlay>();
}
Debug.LogWarning("[GameHUD] 场景中未找到 DamageFlashOverlay,受击泛红特效不会显示。请在场景中放置一个、并把它 On Player Damaged Event 字段接上 OnPlayerDamaged 资产。", this);
}
void OnDestroy()
{
if (_subscribedScoreEvent)
{
ScoreManager.onScoreChanged -= UpdateSoulCount;
_subscribedScoreEvent = false;
}
scoreChangedEvent?.Unregister(UpdateSoulCount);
if (playerHealthVar != null)
playerHealthVar.OnValueChanged -= UpdateLifeIcons;
}
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();
// 事件已在 Start 中无条件订阅,无需逐帧延迟订阅(避免反模式)。
UpdateSkillCooldowns();
}
@@ -140,17 +119,14 @@ namespace GameFramework
/// <summary>
/// 更新生命图标(左下角)。
/// 根据当前血量显示/隐藏对应图标
/// 直接读取 PlayerHealth 共享变量,由 OnValueChanged 事件驱动,无反射、无逐帧 Find
/// </summary>
private void UpdateLifeIcons()
private void UpdateLifeIcons(int value)
{
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;
if (lifeIcons == null || lifeIcons.Length == 0) return;
if (playerHealthVar == null) return;
int currentHealth = value;
for (int i = 0; i < lifeIcons.Length; i++)
{
if (lifeIcons[i] != null)
@@ -160,43 +136,29 @@ namespace GameFramework
/// <summary>
/// 更新技能CD遮罩(右下角)。
/// 使用 Image.fillAmount 实现圆形CD效果
/// 通过各系统的公共只读属性读取冷却状态,替代反射读取私有字段
/// </summary>
private void UpdateSkillCooldowns()
{
// 冲刺 CD(从 PlayerController 读取 rollCooldown + lastRollTime
// 冲刺 CD(从 PlayerController.RollCooldown 读取
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);
var cd = _playerController.RollCooldown;
UpdateSkillCD(skillSprintCD, skillSprintIcon, cd.remaining, cd.total);
}
// 摇铃 CD(从 EchoSystem 读取 cooldown + _lastEchoTime
// 摇铃 CD(从 EchoSystem.BellCooldown 读取
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);
var cd = _echoSystem.BellCooldown;
UpdateSkillCD(skillBellCD, skillBellIcon, cd.remaining, cd.total);
}
// 灵灯 CDSpiritLanternSystem 读取
// 灵灯 CDSpiritLanternSystem 已暴露公共 Cooldown / CooldownRemaining,无反射
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;
float maxCD = _lanternSystem.Cooldown;
UpdateSkillCD(skillLanternCD, skillLanternIcon, remaining, maxCD);
}
}