From be7d511e457de525e213ee7f23e3eed61e955952 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Thu, 23 Jul 2026 15:04:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(enemy):=20=E5=B7=A1=E9=80=BB=E8=B0=83?= =?UTF-8?q?=E8=AF=95=20Inspector=20=E5=8A=A0"=E4=B8=80=E9=94=AE=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E6=8A=A5=E5=91=8A"=E6=8C=89=E9=92=AE(=E4=BE=9B=20AI?= =?UTF-8?q?=20=E8=AF=8A=E6=96=AD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 点击将完整状态以纯文本 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) --- .../Editor/Enemies/EnemyLocomotionEditor.cs | 124 +++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs b/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs index 225e39c5..8c124d8b 100644 --- a/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs +++ b/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs @@ -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}] "); 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;