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
+8
View File
@@ -347,6 +347,7 @@ MonoBehaviour:
maxSightDistance: 4
loseSightTime: 3
listenRange: 15
echoReleasedEvent: {fileID: 11400000, guid: 3f85d8e63d6b82342ba94a4b413c21fa, type: 2}
bellArriveDistance: 1
bellChaseTimeout: 10
bellPatrolTime: 5
@@ -385,9 +386,16 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f9e36f6d2e0ea0541bad29e784e0841b, type: 3}
m_Name:
m_EditorClassIdentifier:
onPlayerDamagedEvent: {fileID: 0}
onPlayerDiedEvent: {fileID: 0}
healthVar: {fileID: 0}
maxHealthVar: {fileID: 0}
maxHealth: 3
deathEffect: {fileID: 0}
isPlayer: 0
dissolveMaterial: {fileID: 0}
fadeDuration: 2
damageInvincibleDuration: 2
--- !u!114 &1893771889199554109
MonoBehaviour:
m_ObjectHideFlags: 0
+7
View File
@@ -355,6 +355,10 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f9e36f6d2e0ea0541bad29e784e0841b, type: 3}
m_Name:
m_EditorClassIdentifier:
onPlayerDamagedEvent: {fileID: 11400000, guid: 1690413250c5d464eb84008d45cb88cc, type: 2}
onPlayerDiedEvent: {fileID: 11400000, guid: 8eee0c771c042c44a88f48756f3717b5, type: 2}
healthVar: {fileID: 11400000, guid: 6952ca1b8005ae541bf172dd398d4cc7, type: 2}
maxHealthVar: {fileID: 11400000, guid: 6952ca1b8005ae541bf172dd398d4cc7, type: 2}
maxHealth: 5
deathEffect: {fileID: 4806121257990350900, guid: f19c76183b5e22e44a73655dc18f1a92, type: 3}
isPlayer: 1
@@ -430,6 +434,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0a44142f1063e1d4dbab7b851d31d258, type: 3}
m_Name:
m_EditorClassIdentifier:
echoReleasedEvent: {fileID: 11400000, guid: 3f85d8e63d6b82342ba94a4b413c21fa, type: 2}
echoKey: 101
expandSpeed: 15
maxRadius: 30
@@ -441,6 +446,8 @@ MonoBehaviour:
ringVisualWidth: 2.5
ringYOffset: 0.1
ringFadeTime: 3
ringExpandSpeed: 15
ringShader: {fileID: 4800000, guid: 67f78fa2aa44cba4cadd869535881366, type: 3}
cooldown: 3
--- !u!1001 &1156712605891213377
PrefabInstance:
@@ -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