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
@@ -2,13 +2,23 @@ using UnityEngine;
using System;
using System.Collections;
using GameFramework;
using Architecture.Core;
using Architecture.Variables;
namespace IndianOceanAssets.Engine2_5D
{
// Handles health, damage, and death for entities
public class HealthSystem : MonoBehaviour
{
/// <summary>玩家受伤时触发(用于受击特效等)</summary>
public static Action onPlayerDamaged;
[Header("SO 事件通道(替代 static onPlayerDamaged 事件)")]
[SerializeField] private GameEvent onPlayerDamagedEvent;
[Header("SO 事件通道(玩家死亡,替代直接调用 GameManager.GameOver")]
[SerializeField] private GameEvent onPlayerDiedEvent;
[Header("SO 变量(供 EnemyHealthBar / HUD 订阅,替代反射读取私有字段)")]
[SerializeField] private IntVariable healthVar;
[SerializeField] private IntVariable maxHealthVar;
[Range(1, 100)]
[SerializeField]
@@ -31,6 +41,7 @@ namespace IndianOceanAssets.Engine2_5D
private bool _isDead = false; // 防止重复触发死亡
private bool _isInvincible = false; // 无敌状态
private float _invincibleTimer = 0f; // 无敌剩余时间(<=0 表示永久无敌)
private bool _warnedDamaged = false; // 防止 onPlayerDamagedEvent 空引用告警刷屏
[Header("受伤无敌")]
[Tooltip("玩家受伤后的无敌时间(秒)")]
@@ -42,13 +53,33 @@ namespace IndianOceanAssets.Engine2_5D
/// <summary>是否处于无敌状态(只读)</summary>
public bool IsInvincible => _isInvincible;
/// <summary>当前血量(只读)</summary>
public int CurrentHealth => health;
/// <summary>最大血量(只读)</summary>
public int MaxHealth => maxHealth;
/// <summary>血量变化事件(current, max)。敌人血条等本地订阅者用于刷新显示,替代反射读取私有字段。</summary>
public event System.Action<int, int> OnHealthChanged;
// 编辑器实时校验:事件字段未接线时给出黄色警告三角 + 控制台告警
private void OnValidate()
{
if (isPlayer && onPlayerDamagedEvent == null)
Debug.LogWarning($"[HealthSystem] 玩家 HealthSystem 的 On Player Damaged Event 未接线({gameObject.name})。受击泛红不会出现。", this);
if (isPlayer && onPlayerDiedEvent == null)
Debug.LogWarning($"[HealthSystem] 玩家 HealthSystem 的 On Player Died Event 未接线({gameObject.name})。玩家死亡不会触发过场。", this);
}
// Initializes health
private void Start()
{
health = maxHealth;
if (isPlayer)
{
if (maxHealthVar != null) maxHealthVar.Value = maxHealth;
if (healthVar != null) healthVar.Value = health;
}
}
// Applies damage and checks for death
@@ -58,10 +89,20 @@ namespace IndianOceanAssets.Engine2_5D
if (_isInvincible) return; // 无敌状态,免疫伤害
health -= damageAmount;
if (isPlayer && healthVar != null) healthVar.Value = health;
OnHealthChanged?.Invoke(health, maxHealth);
// 玩家受伤时触发事件(用于受击泛红特效)
if (isPlayer && onPlayerDamaged != null)
onPlayerDamaged.Invoke();
if (isPlayer)
{
if (onPlayerDamagedEvent != null)
onPlayerDamagedEvent.Raise();
else if (!_warnedDamaged)
{
Debug.LogWarning("[HealthSystem] onPlayerDamagedEvent 未接线!受击泛红特效不会触发。请在玩家 HealthSystem 的 On Player Damaged Event 字段拖入 OnPlayerDamaged 资产。", this);
_warnedDamaged = true;
}
}
// 播放受伤音效
if (isPlayer && AudioManager.Instance != null)
@@ -78,8 +119,8 @@ namespace IndianOceanAssets.Engine2_5D
if (isPlayer)
{
// 玩家死亡:触发过场动画,不立即销毁(由 GameManager 处理
GameManager.GameOver();
// 玩家死亡:通过 OnPlayerDied SO 事件通知 GameManager(解耦,不再直接调用单例
onPlayerDiedEvent?.Raise();
}
else
{
@@ -213,4 +254,4 @@ namespace IndianOceanAssets.Engine2_5D
Destroy(gameObject);
}
}
}
}
@@ -21,6 +21,10 @@ namespace IndianOceanAssets.Engine2_5D
private float lastRollTime; // Timestamp of last roll
private bool isRolling; // Is player currently rolling?
/// <summary>冲刺冷却剩余/总时长(供 HUD 显示,替代反射读取私有字段)。</summary>
public (float remaining, float total) RollCooldown
=> (Mathf.Max(0f, (lastRollTime + rollCooldown) - Time.time), rollCooldown);
[SerializeField] private KeyCode rollKeyCode; // Key to trigger roll
// Enum to switch between sword or projectile attack types