feat(enemy): EnemyLocomotion 运行时巡逻调试(Inspector 面板 + Scene Gizmos)

排查"敌人原地不动"用:PlayMode 下自定义 Inspector 实时显示 模式/策略/是否移动/是否在导航图上/
当前路点与吸附目标/到目标距离vs到达半径/目标是否可达,并按优先级给出定位诊断(模式非Patrol|不在
NavGraph|吸附失败|不可达|已寻路未动)。Scene 视图 Gizmos 画出全部路点连线 + 当前吸附目标(绿/红)+到达半径。

EnemyLocomotion 加 #if UNITY_EDITOR 只读调试访问器 + OnDrawGizmosSelected;记录 _wpResolveOk。
Editor 程序集补 BaseGames.AI 引用(PatrolStrategy/LocomotionMode 所在)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-23 11:57:02 +08:00
co-authored by Claude Opus 4.8
parent 66d100f353
commit 00ee48f750
4 changed files with 221 additions and 1 deletions
@@ -40,6 +40,7 @@ namespace BaseGames.Enemies
private bool _warnedNoWaypoints;
// 当前路点吸附后的"站得住"目标(宽度由 Nav 层解析,本层不感知身体尺寸)
private Vector2 _wpResolvedGoal;
private bool _wpResolveOk; // 上次路点是否成功吸附到导航段(调试用)
// Wander 到点停顿状态
private bool _wanderPausing;
private float _wanderPauseTimer;
@@ -214,7 +215,8 @@ namespace BaseGames.Enemies
private void SetWaypointGoal()
{
Vector2 raw = _waypoints[_wpIndex].position;
if (_enemy.Nav == null || !_enemy.Nav.ResolveStandablePoint(raw, out _wpResolvedGoal))
_wpResolveOk = _enemy.Nav != null && _enemy.Nav.ResolveStandablePoint(raw, out _wpResolvedGoal);
if (!_wpResolveOk)
{
_wpResolvedGoal = raw;
Debug.LogWarning($"[EnemyLocomotion] 路点 '{_waypoints[_wpIndex].name}'(index {_wpIndex}) 无法吸附到任何可行走导航段" +
@@ -236,5 +238,43 @@ namespace BaseGames.Enemies
_wpIndex = (_wpIndex + 1) % _waypoints.Length;
}
}
#if UNITY_EDITOR
// ── 调试只读(供自定义 Inspector / Gizmos,仅编辑器)────────────────────────
public EnemyBase DebugEnemy => _enemy;
public PatrolStrategy DebugStrategy => _patrolStrategy;
public Transform[] DebugWaypoints => _waypoints;
public int DebugWaypointIndex => _wpIndex;
public Vector2 DebugResolvedGoal => _wpResolvedGoal;
public bool DebugResolveOk => _wpResolveOk;
public float DebugArriveRadius => _waypointArriveRadius;
public bool DebugWanderPausing => _wanderPausing;
private void OnDrawGizmosSelected()
{
if (_patrolStrategy != PatrolStrategy.Waypoints || _waypoints == null) return;
// 所有路点 + 连线(编辑期即可见,用于摆点)
Gizmos.color = new Color(0.3f, 0.8f, 1f);
for (int i = 0; i < _waypoints.Length; i++)
{
if (_waypoints[i] == null) continue;
Gizmos.DrawWireSphere(_waypoints[i].position, 0.15f);
var next = _waypoints[(i + 1) % _waypoints.Length];
if (next != null) Gizmos.DrawLine(_waypoints[i].position, next.position);
}
// 运行期:当前吸附目标 + 到达半径 + 连线(绿=吸附成功 红=失败回落原始点)
if (Application.isPlaying && _wpIndex >= 0)
{
Vector3 from = _enemy != null ? _enemy.transform.position : transform.position;
Gizmos.color = _wpResolveOk ? Color.green : Color.red;
Gizmos.DrawWireSphere(_wpResolvedGoal, 0.22f);
Gizmos.DrawLine(from, _wpResolvedGoal);
Gizmos.color = new Color(1f, 0.9f, 0.2f, 0.7f);
Gizmos.DrawWireSphere(_wpResolvedGoal, _waypointArriveRadius);
}
}
#endif
}
}