feat(enemy): Wander 到达随机点后可配置停顿时长
到达一个段内随机游走点后原地停顿 [_wanderPauseMin, _wanderPauseMax] 秒再挑下一个点: - 两个可配置字段(相等=固定时长;都为 0=不停顿立即挑点),每次停顿时长在区间内随机 - 停顿期间切 Idle 步态(否则站着播 Walk=原地踏步),速度归零 - 切换 locomotion 模式时清停顿态,避免残留 实测:移动→到点→停顿(Idle,velX=0)→再出发 循环正常。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,12 @@ namespace BaseGames.Enemies
|
||||
[Header("巡逻策略(Wander/Pace/Waypoints)")]
|
||||
[SerializeField] private PatrolStrategy _patrolStrategy = PatrolStrategy.Wander;
|
||||
|
||||
[Header("Wander 策略参数")]
|
||||
[Tooltip("到达一个随机游走点后的停顿时长下限(秒)。停顿期间原地待机(播 Idle),再挑下一个点。")]
|
||||
[SerializeField] [Min(0f)] private float _wanderPauseMin = 0.5f;
|
||||
[Tooltip("到达一个随机游走点后的停顿时长上限(秒)。与下限相同=固定时长;两者都为 0=不停顿、立即挑下一个点。")]
|
||||
[SerializeField] [Min(0f)] private float _wanderPauseMax = 1.5f;
|
||||
|
||||
[Header("Waypoints 策略参数")]
|
||||
[SerializeField] private Transform[] _waypoints;
|
||||
[SerializeField] private bool _pingPong;
|
||||
@@ -32,6 +38,9 @@ namespace BaseGames.Enemies
|
||||
private int _wpIndex = -1;
|
||||
private int _wpDir = 1;
|
||||
private bool _warnedNoWaypoints;
|
||||
// Wander 到点停顿状态
|
||||
private bool _wanderPausing;
|
||||
private float _wanderPauseTimer;
|
||||
|
||||
public LocomotionMode CurrentMode => _mode;
|
||||
public bool IsMoving => _enemy != null && _enemy.Nav != null && _enemy.Nav.IsMoving;
|
||||
@@ -48,6 +57,7 @@ namespace BaseGames.Enemies
|
||||
{
|
||||
if (_mode == mode) return;
|
||||
_mode = mode;
|
||||
_wanderPausing = false; // 切换模式清 Wander 停顿态
|
||||
if (mode == LocomotionMode.Idle) _enemy?.StopMovement();
|
||||
if (mode == LocomotionMode.Patrol && _enemy?.Stats != null)
|
||||
_enemy.Nav?.SetSpeed(_enemy.Stats.WalkSpeed);
|
||||
@@ -83,11 +93,16 @@ namespace BaseGames.Enemies
|
||||
private void Update()
|
||||
{
|
||||
if (_enemy == null) return;
|
||||
if (!_gaitInit || _gaitMode != _mode)
|
||||
// Wander 到点停顿期间用 Idle 步态(原地待机,否则站着播 Walk=原地踏步)。
|
||||
LocomotionMode gait = (_mode == LocomotionMode.Patrol
|
||||
&& _patrolStrategy == PatrolStrategy.Wander
|
||||
&& _wanderPausing)
|
||||
? LocomotionMode.Idle : _mode;
|
||||
if (!_gaitInit || _gaitMode != gait)
|
||||
{
|
||||
_enemy.PlayLocomotionClip(_mode);
|
||||
_enemy.PlayLocomotionClip(gait);
|
||||
// 招式动画锁定期内不算"已应用",解锁后重新同步步态(否则 Skill_End 后卡在收招动画)
|
||||
if (!_enemy.IsAnimLocked) { _gaitMode = _mode; _gaitInit = true; }
|
||||
if (!_enemy.IsAnimLocked) { _gaitMode = gait; _gaitInit = true; }
|
||||
}
|
||||
switch (_mode)
|
||||
{
|
||||
@@ -106,14 +121,38 @@ namespace BaseGames.Enemies
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 段内随机游走:随机点限制在当前所站的导航段上(不跨 NavLink,避免崖边夹停/卡 link),
|
||||
/// 到达一个点后原地停顿 [_wanderPauseMin, _wanderPauseMax] 秒(期间播 Idle),再挑下一个点。
|
||||
/// </summary>
|
||||
private void TickWander()
|
||||
{
|
||||
if (_enemy.Nav == null) return;
|
||||
|
||||
// 移动中:清停顿态,等到达
|
||||
if (_enemy.Nav.IsMoving) { _wanderPausing = false; return; }
|
||||
|
||||
// 已到达(或尚未出发):先停顿计时,再挑下一个点
|
||||
if (!_wanderPausing)
|
||||
{
|
||||
_wanderPausing = true;
|
||||
_wanderPauseTimer = Random.Range(_wanderPauseMin, Mathf.Max(_wanderPauseMin, _wanderPauseMax));
|
||||
}
|
||||
|
||||
_wanderPauseTimer -= Time.deltaTime;
|
||||
if (_wanderPauseTimer <= 0f)
|
||||
{
|
||||
_wanderPausing = false;
|
||||
_enemy.Nav.WalkToRandomOnSegment();
|
||||
}
|
||||
}
|
||||
|
||||
private void TickPatrol()
|
||||
{
|
||||
switch (_patrolStrategy)
|
||||
{
|
||||
case PatrolStrategy.Wander:
|
||||
// 段内随机游走:随机点限制在当前所站的导航段上,不跨越 NavLink,
|
||||
// 避免地面单位游走时被崖边夹停或卡在过不去的跳/落 link 处。
|
||||
if (_enemy.Nav != null && !_enemy.Nav.IsMoving) _enemy.Nav.WalkToRandomOnSegment();
|
||||
TickWander();
|
||||
break;
|
||||
|
||||
case PatrolStrategy.Pace: // 撞墙/悬崖翻向的来回踱步(速度驱动,不依赖 nav)
|
||||
|
||||
Reference in New Issue
Block a user