#if UNITY_EDITOR || DEVELOPMENT_BUILD using UnityEngine; using BaseGames.Enemies.Abilities; using BaseGames.Enemies.StatusEffects; namespace BaseGames.Enemies { /// /// 运行时 AI 状态调试叠加层(仅 Editor)。 /// 挂到敌人 Prefab 根节点,运行时在场景视图 / GameView 上显示: /// - AiPhase / EnemyStateType /// - 当前激活的能力名称及运行状态 /// - LOS 状态 / 到玩家距离 /// - Boss:技能执行状态 + 各技能冷却倒计时 /// [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(); _boss = GetComponent(); _statusMgr = GetComponent(); } // ── 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 视图 GUI(OnGUI)────────────────────────────────────────── 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 BuildLines() { var list = new System.Collections.Generic.List(8); // 名称 list.Add($"{gameObject.name}"); // AiPhase + EnemyState list.Add($"Phase: {_enemy.CurrentAiPhase} State: {_enemy.CurrentState}"); // 距离 + 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: {los}"); // 当前激活能力 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: {abilityId} [{ab.Phase}]"); } } } // Boss 信息 if (_boss != null) { list.Add($"BossSkillExec: {_boss.IsBossSkillExecuting}"); } // 状态效果 if (_statusMgr != null) { var effects = _statusMgr.ActiveEffects; for (int i = 0; i < effects.Count; i++) list.Add($"FX: {effects[i].Type}"); } 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