feat(enemy): 巡逻调试 Inspector 加"一键打印报告"按钮(供 AI 诊断)
点击将完整状态以纯文本 Debug.Log 到 Console,可直接复制给 AI:AI决策层(EnemyState/ BrainGraph 当前状态名/LocomotionMode)、移动导航(Nav类型/IsMoving/IsOnLink/HasValidPosition/ PB2d Position/EdgeSafeMargin/_maxSnapDistance)、Waypoints(当前索引/吸附目标/CanReach/逐路点 实时吸附检测 raw→snap 及垂直差)、文末一句根因诊断。编辑期打印静态配置,Play 期打印运行时全量。 实测输出直指根因:Brain='Idle_Disguise' → LocomotionMode=Idle → TickPatrol 不执行。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using BaseGames.AI;
|
||||
@@ -46,10 +47,21 @@ namespace BaseGames.Editor
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
EditorGUILayout.Space(6f);
|
||||
// 一键打印完整调试报告到 Console(可复制粘贴给 AI 诊断)
|
||||
var prevBg = GUI.backgroundColor;
|
||||
GUI.backgroundColor = new Color(0.5f, 0.8f, 1f);
|
||||
if (GUILayout.Button("📋 打印调试信息(复制给 AI 诊断)", GUILayout.Height(26f)))
|
||||
{
|
||||
var loco = (EnemyLocomotion)target;
|
||||
Debug.Log(BuildReport(loco), loco);
|
||||
}
|
||||
GUI.backgroundColor = prevBg;
|
||||
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
EditorGUILayout.Space(4f);
|
||||
EditorGUILayout.HelpBox("进入 Play 模式后此处显示实时巡逻调试信息。", MessageType.None);
|
||||
EditorGUILayout.HelpBox("进入 Play 模式后此处显示实时巡逻调试信息(按钮在编辑期也可打印静态配置)。", MessageType.None);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,6 +69,116 @@ namespace BaseGames.Editor
|
||||
DrawRuntimeSection();
|
||||
}
|
||||
|
||||
// ── 一键调试报告(纯文本,便于复制给 AI)────────────────────────────
|
||||
private static string BuildReport(EnemyLocomotion loco)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var enemy = loco.DebugEnemy;
|
||||
var go = loco.gameObject;
|
||||
|
||||
sb.AppendLine("========== EnemyLocomotion 调试报告 ==========");
|
||||
sb.AppendLine($"对象: {go.name} 场景: {go.scene.name} Playing: {Application.isPlaying} t={Time.time:F2}");
|
||||
|
||||
// ── AI 决策层 ──
|
||||
sb.AppendLine("── AI 决策层 ──");
|
||||
if (enemy != null)
|
||||
{
|
||||
string brainState = enemy.Brain != null ? enemy.Brain.CurrentStateName : "(无 EnemyAiBrain)";
|
||||
sb.AppendLine($"EnemyState(控制态): {enemy.CurrentState}");
|
||||
sb.AppendLine($"Brain 状态(BrainGraph): {brainState}");
|
||||
}
|
||||
else sb.AppendLine("EnemyBase 未解析(编辑期或 Awake 未运行)");
|
||||
sb.AppendLine($"LocomotionMode: {loco.CurrentMode} (仅 Patrol 时才跑 TickPatrol)");
|
||||
|
||||
// ── 移动 / 导航 ──
|
||||
sb.AppendLine("── 移动/导航 ──");
|
||||
sb.AppendLine($"PatrolStrategy: {loco.DebugStrategy}");
|
||||
sb.AppendLine($"Loco.IsMoving: {loco.IsMoving} WanderPausing: {loco.DebugWanderPausing}");
|
||||
|
||||
var nav = enemy != null ? enemy.Nav : null;
|
||||
if (nav != null)
|
||||
{
|
||||
sb.AppendLine($"Nav 类型: {nav.GetType().Name} Nav.IsMoving: {nav.IsMoving} IsOnLink: {nav.IsOnLink} LinkType: {nav.CurrentLinkType}");
|
||||
if (nav is EnemyNavAgent ena && ena.RawNavAgent != null)
|
||||
{
|
||||
var raw = ena.RawNavAgent;
|
||||
sb.AppendLine($"在导航图上(HasValidPosition): {raw.HasValidPosition} IsFollowingAPath: {raw.IsFollowingAPath}");
|
||||
sb.AppendLine($"PB2d Agent Position: {raw.Position}");
|
||||
}
|
||||
}
|
||||
else sb.AppendLine("Nav: null");
|
||||
|
||||
var mv = enemy != null ? enemy.Movement : null;
|
||||
if (mv != null) sb.AppendLine($"EdgeSafeMargin(身体半宽+偏移): {mv.EdgeSafeMargin:F3}");
|
||||
sb.AppendLine($"_maxSnapDistance: {ReadMaxSnap(nav)}");
|
||||
if (enemy != null) sb.AppendLine($"EnemyPos: {enemy.transform.position}");
|
||||
|
||||
// ── Waypoints ──
|
||||
if (loco.DebugStrategy == PatrolStrategy.Waypoints)
|
||||
{
|
||||
sb.AppendLine("── Waypoints ──");
|
||||
var wps = loco.DebugWaypoints;
|
||||
int cnt = wps != null ? wps.Length : 0;
|
||||
int idx = loco.DebugWaypointIndex;
|
||||
sb.AppendLine($"当前索引: {idx} / {(cnt > 0 ? cnt - 1 : 0)} ArriveRadius: {loco.DebugArriveRadius:F2}");
|
||||
Vector2 goal = loco.DebugResolvedGoal;
|
||||
sb.AppendLine($"当前吸附目标(Resolved): {goal} ResolveOk: {loco.DebugResolveOk}");
|
||||
if (enemy != null)
|
||||
sb.AppendLine($"到目标距离: {Vector2.Distance(enemy.transform.position, goal):F2}" +
|
||||
(nav != null ? $" CanReach: {nav.CanReach(goal)}" : ""));
|
||||
|
||||
if (wps != null)
|
||||
{
|
||||
sb.AppendLine("路点逐个吸附检测(点击时实时计算):");
|
||||
for (int i = 0; i < wps.Length; i++)
|
||||
{
|
||||
if (wps[i] == null) { sb.AppendLine($" [{i}] <NULL 引用>"); continue; }
|
||||
Vector2 rawp = wps[i].position;
|
||||
string snapStr = "(需 Play 且有 Nav)";
|
||||
if (nav != null)
|
||||
{
|
||||
bool ok = nav.ResolveStandablePoint(rawp, out var snapped);
|
||||
snapStr = $"snap ok={ok} {snapped} |rawY-snapY|={Mathf.Abs(rawp.y - snapped.y):F2}";
|
||||
}
|
||||
sb.AppendLine($" [{i}] {wps[i].name} raw={rawp} -> {snapStr}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 诊断 ──
|
||||
sb.AppendLine("── 诊断 ──");
|
||||
sb.AppendLine(Diagnose(loco, enemy, nav));
|
||||
sb.AppendLine("=============================================");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string ReadMaxSnap(IPathAgent nav)
|
||||
{
|
||||
if (nav is EnemyNavAgent ena)
|
||||
{
|
||||
using var so = new SerializedObject(ena);
|
||||
var msd = so.FindProperty("_maxSnapDistance");
|
||||
if (msd != null) return msd.floatValue.ToString("F1");
|
||||
}
|
||||
return "(n/a)";
|
||||
}
|
||||
|
||||
// 纯文本诊断(与面板 HelpBox 同一判定逻辑,供报告文末结论)
|
||||
private static string Diagnose(EnemyLocomotion loco, EnemyBase enemy, IPathAgent nav)
|
||||
{
|
||||
if (!Application.isPlaying) return "编辑期:仅静态配置,进入 Play 后可判定运行时问题。";
|
||||
if (loco.CurrentMode != LocomotionMode.Patrol)
|
||||
return $"→ 不动主因:LocomotionMode={loco.CurrentMode}(非 Patrol)。AI 尚未进入巡逻态,TickPatrol 不执行。" +
|
||||
(enemy?.Brain != null ? $" 当前 Brain 状态='{enemy.Brain.CurrentStateName}'——检查该状态是否/何时切到巡逻。" : "");
|
||||
bool mapped = !(nav is EnemyNavAgent e && e.RawNavAgent != null) || ((EnemyNavAgent)nav).RawNavAgent.HasValidPosition;
|
||||
if (!mapped) return "→ 敌人不在 NavGraph 上(HasValidPosition=false):RequestMoveTo 被忽略。检查贴地/是否在已 bake 的 NavSurface。";
|
||||
if (loco.DebugStrategy == PatrolStrategy.Waypoints && !loco.DebugResolveOk)
|
||||
return "→ 当前路点吸附失败:超出 _maxSnapDistance。调大或把路点摆近平台。";
|
||||
if (nav != null && !nav.CanReach(loco.DebugResolvedGoal))
|
||||
return "→ 目标不可达:与吸附目标不连通,或隔着不可穿越 NavLink。";
|
||||
return "→ 巡逻链路各项正常(若仍不动,检查 EnemyMovement 速度/受阻/刚体约束)。";
|
||||
}
|
||||
|
||||
private void DrawRuntimeSection()
|
||||
{
|
||||
var loco = (EnemyLocomotion)target;
|
||||
|
||||
Reference in New Issue
Block a user