refactor(enemy): 三处卡死检测收敛为 StallDetector;修复 Pace 持续受阻时无法掉头的死锁

This commit is contained in:
2026-07-28 11:04:18 +08:00
parent 5cfacf199a
commit c929b5efb7
3 changed files with 25 additions and 54 deletions
@@ -91,9 +91,7 @@ namespace BaseGames.Enemies.Navigation
private bool _redirectedToClosest; // 本轮已改道到最近点,避免每帧重复下发
private bool _obstructionSignal; // 受阻信号(寻路失败/卡死),延后到 FixedUpdate 统一处理
// 卡死检测窗口
private float _stuckWinTimer;
private float _stuckWinStartDist;
private bool _stuckWinInit;
private StallDetector _segStall;
// 连接段状态缓存
private NavLinkType _currentLinkType = NavLinkType.None;
@@ -121,6 +119,8 @@ namespace BaseGames.Enemies.Navigation
_navAgent.OnSegmentTraversal += HandleSegmentTraversal;
_navAgent.OnFailedToFindPath += HandlePathFailed;
_navAgent.OnReachedGoal += HandleGoalReached;
_segStall = new StallDetector(_stuckWindowSeconds, _stuckMinProgress);
}
private void OnDestroy()
@@ -327,30 +327,16 @@ namespace BaseGames.Enemies.Navigation
_wasNavOnSegment = onSegment;
}
// 卡死检测:段上移动时,滑动窗口内朝子目标推进不足阈值 → 判受阻(覆盖"障碍未进图"的物理空推)
// 卡死检测:段上移动时,窗口内净水平位移不足 → 判受阻覆盖"障碍未进图"的物理空推
// 原实现比较"到子目标的距离缩小量",现统一为净水平位移:不依赖子目标,且绕行时不会误判为卡死。
private void TickStuckDetection(bool onSegment)
{
if (!onSegment) { _stuckWinInit = false; return; }
float dist = Mathf.Abs(_navAgent.PathSubGoal.x - transform.position.x);
if (!_stuckWinInit)
{
_stuckWinInit = true;
_stuckWinTimer = 0f;
_stuckWinStartDist = dist;
return;
}
_stuckWinTimer += Time.fixedDeltaTime;
if (_stuckWinTimer >= _stuckWindowSeconds)
{
if (_stuckWinStartDist - dist < _stuckMinProgress)
_obstructionSignal = true; // 窗口内几乎没靠近子目标 → 卡死
_stuckWinTimer = 0f;
_stuckWinStartDist = dist;
}
if (!onSegment) { _segStall.Reset(transform.position.x); return; }
if (_segStall.Tick(transform.position.x, Time.fixedDeltaTime))
_obstructionSignal = true; // 窗口内几乎没推进 → 卡死
}
private void ResetStuckWindow() => _stuckWinInit = false;
private void ResetStuckWindow() => _segStall.Reset(transform.position.x);
// 统一受阻处理:尽力改道到最近可达点(走到障碍前),拿不到更近点则停下,交由意图层结算。
private void ProcessObstruction()