From c43ca8e8b6c1c5f2e9add63e01dd6b4c801fdefa Mon Sep 17 00:00:00 2001 From: Joywayer Date: Fri, 10 Jul 2026 13:47:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(enemy):=20EnemyLocomotion=20=E5=B7=A1?= =?UTF-8?q?=E9=80=BB=E7=AD=96=E7=95=A5=20Pace(=E8=B8=B1=E6=AD=A5)=20+=20Wa?= =?UTF-8?q?ypoints(=E8=B7=AF=E7=82=B9=20loop/ping-pong)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Enemies/Navigation/EnemyLocomotion.cs | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs b/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs index e0e7243e..80f3fe73 100644 --- a/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs +++ b/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs @@ -11,15 +11,23 @@ namespace BaseGames.Enemies [DisallowMultipleComponent] public sealed class EnemyLocomotion : MonoBehaviour, IEnemyLocomotion { - [Header("巡逻策略(P6 实现 Pace/Waypoints;当前仅 Wander)")] + [Header("巡逻策略(Wander/Pace/Waypoints)")] [SerializeField] private PatrolStrategy _patrolStrategy = PatrolStrategy.Wander; + [Header("Waypoints 策略参数")] + [SerializeField] private Transform[] _waypoints; + [SerializeField] private bool _pingPong; + private EnemyBase _enemy; private LocomotionMode _mode = LocomotionMode.Idle; private Transform _approachTarget; private Vector2 _facePoint; private LocomotionMode _gaitMode; private bool _gaitInit; + private float _paceDir = 1f; + private int _wpIndex = -1; + private int _wpDir = 1; + private bool _warnedNoWaypoints; public LocomotionMode CurrentMode => _mode; public bool IsMoving => _enemy != null && _enemy.Nav != null && _enemy.Nav.IsMoving; @@ -96,17 +104,48 @@ namespace BaseGames.Enemies private void TickPatrol() { - var nav = _enemy.Nav; - if (nav == null) return; switch (_patrolStrategy) { case PatrolStrategy.Wander: - if (!nav.IsMoving) nav.WalkToRandom(); + if (_enemy.Nav != null && !_enemy.Nav.IsMoving) _enemy.Nav.WalkToRandom(); break; - // Pace / Waypoints:P6 实现 - default: - if (!nav.IsMoving) nav.WalkToRandom(); + + case PatrolStrategy.Pace: // 撞墙/悬崖翻向的来回踱步(速度驱动,不依赖 nav) + var mv = _enemy.Movement; + if (mv != null && (mv.IsWallAhead || mv.IsLedgeAhead)) _paceDir = -_paceDir; + _enemy.MoveInDirection(_paceDir); 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; } } }