feat(enemy): EnemyLocomotion 巡逻策略 Pace(踱步) + Waypoints(路点 loop/ping-pong)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 13:47:14 +08:00
co-authored by Claude Opus 4.8
parent f7554b1a65
commit c43ca8e8b6
@@ -11,15 +11,23 @@ namespace BaseGames.Enemies
[DisallowMultipleComponent] [DisallowMultipleComponent]
public sealed class EnemyLocomotion : MonoBehaviour, IEnemyLocomotion public sealed class EnemyLocomotion : MonoBehaviour, IEnemyLocomotion
{ {
[Header("巡逻策略(P6 实现 Pace/Waypoints;当前仅 Wander")] [Header("巡逻策略(Wander/Pace/Waypoints")]
[SerializeField] private PatrolStrategy _patrolStrategy = PatrolStrategy.Wander; [SerializeField] private PatrolStrategy _patrolStrategy = PatrolStrategy.Wander;
[Header("Waypoints 策略参数")]
[SerializeField] private Transform[] _waypoints;
[SerializeField] private bool _pingPong;
private EnemyBase _enemy; private EnemyBase _enemy;
private LocomotionMode _mode = LocomotionMode.Idle; private LocomotionMode _mode = LocomotionMode.Idle;
private Transform _approachTarget; private Transform _approachTarget;
private Vector2 _facePoint; private Vector2 _facePoint;
private LocomotionMode _gaitMode; private LocomotionMode _gaitMode;
private bool _gaitInit; private bool _gaitInit;
private float _paceDir = 1f;
private int _wpIndex = -1;
private int _wpDir = 1;
private bool _warnedNoWaypoints;
public LocomotionMode CurrentMode => _mode; public LocomotionMode CurrentMode => _mode;
public bool IsMoving => _enemy != null && _enemy.Nav != null && _enemy.Nav.IsMoving; public bool IsMoving => _enemy != null && _enemy.Nav != null && _enemy.Nav.IsMoving;
@@ -96,17 +104,48 @@ namespace BaseGames.Enemies
private void TickPatrol() private void TickPatrol()
{ {
var nav = _enemy.Nav;
if (nav == null) return;
switch (_patrolStrategy) switch (_patrolStrategy)
{ {
case PatrolStrategy.Wander: case PatrolStrategy.Wander:
if (!nav.IsMoving) nav.WalkToRandom(); if (_enemy.Nav != null && !_enemy.Nav.IsMoving) _enemy.Nav.WalkToRandom();
break; break;
// Pace / WaypointsP6 实现
default: case PatrolStrategy.Pace: // 撞墙/悬崖翻向的来回踱步(速度驱动,不依赖 nav)
if (!nav.IsMoving) nav.WalkToRandom(); var mv = _enemy.Movement;
if (mv != null && (mv.IsWallAhead || mv.IsLedgeAhead)) _paceDir = -_paceDir;
_enemy.MoveInDirection(_paceDir);
break; break;
case PatrolStrategy.Waypoints: // 按序(loop 或 ping-pong)巡逻路点
if (_waypoints == null || _waypoints.Length == 0)
{
if (!_warnedNoWaypoints)
{
Debug.LogWarning("[EnemyLocomotion] PatrolStrategy=Waypoints 但未配置 _waypoints,无法巡逻。", this);
_warnedNoWaypoints = true;
}
break;
}
if (_enemy.Nav != null && !_enemy.Nav.IsMoving)
{
AdvanceWaypoint();
_enemy.MoveTo(_waypoints[_wpIndex].position);
}
break;
}
}
private void AdvanceWaypoint()
{
if (_pingPong)
{
int next = _wpIndex + _wpDir;
if (next < 0 || next >= _waypoints.Length) { _wpDir = -_wpDir; next = _wpIndex + _wpDir; }
_wpIndex = Mathf.Clamp(next, 0, _waypoints.Length - 1);
}
else
{
_wpIndex = (_wpIndex + 1) % _waypoints.Length;
} }
} }
} }