Files
zeling_v2/Assets/_Game/Scripts/Enemies/EnemyDebugOverlay.cs
Joywayer 06048c966a feat: Add HurtBoxOwnerGuard to prevent multiple damage registrations from the same HitBox activation
- Implemented HurtBoxOwnerGuard to ensure that multiple HurtBoxes on the same character do not register damage multiple times during a single HitBox activation.
- Added custom editor for HitBox to facilitate the creation of shape colliders with HitBoxColliderProxy.
- Introduced PhysicsPerceptionSystem for enemy perception, supporting multiple detection modes including RangeCircle, BatchLOS, FanCast, and BoxCast.
- Created EnemyPatrolZone to define patrol and chase areas for enemies, allowing for shared zones among multiple enemies.
- Added BD_IsOutsideZone conditional task for Behavior Designer to check if an enemy or player is outside a defined patrol zone.
2026-06-02 16:10:44 +08:00

151 lines
5.6 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.
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine;
using BaseGames.Enemies.Abilities;
using BaseGames.Enemies.StatusEffects;
namespace BaseGames.Enemies
{
/// <summary>
/// 运行时 AI 状态调试叠加层(仅 Editor
/// 挂到敌人 Prefab 根节点,运行时在场景视图 / GameView 上显示:
/// - AiPhase / EnemyStateType
/// - 当前激活的能力名称及运行状态
/// - LOS 状态 / 到玩家距离
/// - Boss技能执行状态 + 各技能冷却倒计时
/// </summary>
[AddComponentMenu("BaseGames/Debug/Enemy Debug Overlay")]
public sealed class EnemyDebugOverlay : MonoBehaviour
{
[Header("显示选项")]
[Tooltip("在 Scene 视图中绘制 GUI 标签")]
[SerializeField] private bool _showInSceneView = true;
[Tooltip("在 Game 视图中绘制 GUI 标签(需要 OnGUI")]
[SerializeField] private bool _showInGameView = false;
[Tooltip("文字背景透明度")]
[SerializeField, Range(0f, 1f)] private float _bgAlpha = 0.65f;
private EnemyBase _enemy;
private BossBase _boss;
private EnemyStatusEffectManager _statusMgr;
private GUIStyle _labelStyle;
private GUIStyle _boxStyle;
private bool _stylesInit;
private void Awake()
{
_enemy = GetComponent<EnemyBase>();
_boss = GetComponent<BossBase>();
_statusMgr = GetComponent<EnemyStatusEffectManager>();
}
// ── Scene 视图标签UnityEditor.Handles.Label─────────────────────
private void OnDrawGizmos()
{
if (!_showInSceneView || !Application.isPlaying || _enemy == null) return;
var lines = BuildLines();
var label = string.Join("\n", lines);
var pos = transform.position + Vector3.up * 2.2f;
UnityEditor.Handles.Label(pos, label);
}
// ── Game 视图 GUIOnGUI──────────────────────────────────────────
private void OnGUI()
{
if (!_showInGameView || !Application.isPlaying || _enemy == null) return;
InitStyles();
var cam = UnityEngine.Camera.main;
if (cam == null) return;
Vector3 worldPos = transform.position + Vector3.up * 2.2f;
Vector3 screen = cam.WorldToScreenPoint(worldPos);
if (screen.z < 0f) return;
float screenY = Screen.height - screen.y;
var lines = BuildLines();
var text = string.Join("\n", lines);
var size = _labelStyle.CalcSize(new GUIContent(text));
var rect = new Rect(screen.x - size.x * 0.5f, screenY - size.y, size.x + 8f, size.y + 4f);
GUI.Box(rect, GUIContent.none, _boxStyle);
GUI.Label(rect, text, _labelStyle);
}
// ── 内部 ──────────────────────────────────────────────────────────
private System.Collections.Generic.List<string> BuildLines()
{
var list = new System.Collections.Generic.List<string>(8);
// 名称
list.Add($"<b>{gameObject.name}</b>");
// AiPhase + EnemyState
list.Add($"Phase: <color=#ffcc44>{_enemy.CurrentAiPhase}</color> State: <color=#88ddff>{_enemy.CurrentState}</color>");
// 距离 + LOS
float dist = _enemy.Stats != null ? Mathf.Sqrt(_enemy.Stats.SqrDistanceToPlayer) : -1f;
bool los = _enemy.IsPlayerVisible();
var losColor = los ? "#44ff88" : "#ff6644";
list.Add($"Dist: {dist:F1}m LOS: <color={losColor}>{los}</color>");
// 当前激活能力
if (_enemy.Abilities != null)
{
foreach (var ab in _enemy.Abilities.All)
{
if (ab.IsRunning)
{
string abilityId = ab.Config?.abilityId ?? ab.GetType().Name;
list.Add($"Ability: <color=#ffaa44>{abilityId}</color> [{ab.Phase}]");
}
}
}
// Boss 信息
if (_boss != null)
{
list.Add($"BossSkillExec: <color={( _boss.IsBossSkillExecuting ? "#ff4444" : "#aaaaaa")}>{_boss.IsBossSkillExecuting}</color>");
}
// 状态效果
if (_statusMgr != null)
{
var effects = _statusMgr.ActiveEffects;
for (int i = 0; i < effects.Count; i++)
list.Add($"FX: <color=#dd88ff>{effects[i].Type}</color>");
}
return list;
}
private void InitStyles()
{
if (_stylesInit) return;
_stylesInit = true;
_labelStyle = new GUIStyle(GUI.skin.label)
{
richText = true,
fontSize = 11,
alignment = TextAnchor.UpperLeft,
padding = new RectOffset(4, 4, 2, 2),
};
_labelStyle.normal.textColor = Color.white;
var bgTex = new Texture2D(1, 1);
bgTex.SetPixel(0, 0, new Color(0.05f, 0.05f, 0.1f, _bgAlpha));
bgTex.Apply();
_boxStyle = new GUIStyle(GUI.skin.box);
_boxStyle.normal.background = bgTex;
}
}
}
#endif