refactor(enemy): FlyingDirectNavigator 改名 FlyingNavigator 并切换到 IEnemyNavigator

This commit is contained in:
2026-07-28 11:17:34 +08:00
parent c041712e99
commit 49627d357d
2 changed files with 33 additions and 69 deletions
@@ -1,25 +1,23 @@
using System;
using UnityEngine; using UnityEngine;
using BaseGames.Enemies; using BaseGames.Enemies;
namespace BaseGames.Enemies.Navigation namespace BaseGames.Enemies.Navigation
{ {
/// <summary> /// <summary>
/// 飞行单位直线导航代理。 /// 飞行单位直线导航代理,实现 <see cref="IEnemyNavigator"/>
/// 不依赖 PathBerserker2d 寻路,直接通过 Rigidbody2D.MovePosition 向目标点直飞。 /// 不依赖寻路网格,直接通过 Rigidbody2D.MovePosition 向目标点直飞。
/// ///
/// 特性: /// 特性:
/// <list type="bullet"> /// <list type="bullet">
/// <item>空闲时施加正弦波形悬停偏移,产生飘浮感。</item> /// <item>空闲时施加正弦波形悬停偏移,产生飘浮感。</item>
/// <item>NavLink 相关成员均返回安全默认值(飞行单位不使用平台连接段)。</item> /// <item>飞行单位直飞,无地形阻挡概念,<see cref="IsBlocked"/> 恒为 false。</item>
/// <item>目标到达后触发 <see cref="OnGoalReached"/> 事件(替代轮询 IsAtDestination)。</item>
/// </list> /// </list>
/// ///
/// 使用方式:挂载到飞行怪 Prefab 根节点,替代 EnemyNavAgent /// 使用方式:挂载到飞行怪 Prefab 根节点,替代 EnemyNavAgent
/// <see cref="EnemyBase.Awake"/> 的 GetComponent&lt;IPathAgent&gt;() 自动发现此组件。 /// <see cref="EnemyBase.Awake"/> 的 GetComponent&lt;IEnemyNavigator&gt;() 自动发现此组件。
/// </summary> /// </summary>
[RequireComponent(typeof(Rigidbody2D))] [RequireComponent(typeof(Rigidbody2D))]
public sealed class FlyingDirectNavigator : MonoBehaviour, IPathAgent public sealed class FlyingNavigator : MonoBehaviour, IEnemyNavigator
{ {
[Header("移动参数")] [Header("移动参数")]
[Tooltip("直飞速度(m/s")] [Tooltip("直飞速度(m/s")]
@@ -27,7 +25,7 @@ namespace BaseGames.Enemies.Navigation
[Tooltip("到达目标的判定距离(m")] [Tooltip("到达目标的判定距离(m")]
[SerializeField] private float _stoppingDist = 0.15f; [SerializeField] private float _stoppingDist = 0.15f;
[Header("游荡参数(WalkToRandom")] [Header("游荡参数(TryPickWanderPoint")]
[Tooltip("随机游荡目标点距当前位置的最大半径(m)。")] [Tooltip("随机游荡目标点距当前位置的最大半径(m)。")]
[Min(0.1f)] [Min(0.1f)]
[SerializeField] private float _randomWalkRadius = 3f; [SerializeField] private float _randomWalkRadius = 3f;
@@ -42,33 +40,23 @@ namespace BaseGames.Enemies.Navigation
[Tooltip("横向方向翻转周期(s")] [Tooltip("横向方向翻转周期(s")]
[SerializeField] private float _hoverFlipInterval = 1.5f; [SerializeField] private float _hoverFlipInterval = 1.5f;
// ── IPathAgent 事件 ────────────────────────────────────────────
public event Action<NavLinkType> OnLinkStarted { add { } remove { } }
public event Action<NavLinkType> OnLinkCompleted { add { } remove { } }
public event Action OnNavPathFailed { add { } remove { } }
public event Action OnGoalReached;
// ── 状态 ─────────────────────────────────────────────────────── // ── 状态 ───────────────────────────────────────────────────────
private Rigidbody2D _rb; private Rigidbody2D _rb;
private EnemyMovement _movement; private EnemyMovement _movement;
private Vector2? _destination; private Vector2 _target;
private bool _isMoving; private bool _hasTarget;
private bool _goalFired; private bool _arrived;
private float _hoverTimer; private float _hoverTimer;
private float _hoverFlipTimer; private float _hoverFlipTimer;
private float _hoverDir = 1f; private float _hoverDir = 1f;
// ── IPathAgent 属性 ──────────────────────────────────────────── // ── IEnemyNavigator 属性 ────────────────────────────────────────
public bool IsMoving => _isMoving; public bool IsMoving => _hasTarget && !_arrived;
public bool IsOnLink => false; public bool HasArrived => _arrived;
public NavLinkType CurrentLinkType => NavLinkType.None;
public Vector2 CurrentLinkStart => Vector2.zero;
public Vector2 CurrentLinkEnd => Vector2.zero;
// 飞行单位直飞、始终可达,无"受阻/最近可达点"概念。 // 飞行单位直飞,无地形阻挡概念。
public bool LastMoveObstructed => false; public bool IsBlocked => false;
public bool TryGetClosestReachable(out Vector2 worldPos) { worldPos = Vector2.zero; return false; }
// ── Unity 生命周期 ───────────────────────────────────────────── // ── Unity 生命周期 ─────────────────────────────────────────────
private void Awake() private void Awake()
@@ -81,56 +69,38 @@ namespace BaseGames.Enemies.Navigation
private void FixedUpdate() private void FixedUpdate()
{ {
if (_destination.HasValue) if (_hasTarget && !_arrived)
UpdateChase(); UpdateChase();
else else
UpdateHover(); UpdateHover();
} }
// ── IPathAgent 方法 ──────────────────────────────────────────── // ── IEnemyNavigator 方法 ────────────────────────────────────────
public void RequestMoveTo(Vector2 target) public void MoveTowards(Vector2 target)
{ {
_destination = target; // 幂等:同目标重复调用不重置状态(避免每帧重发目标导致已到达状态被反复清空)。
_isMoving = true; if (_hasTarget && (target - _target).sqrMagnitude <= 1e-6f)
_goalFired = false; return;
_target = target;
_hasTarget = true;
_arrived = false;
} }
public void StopNavigation() public void Stop()
{ {
_destination = null; _hasTarget = false;
_isMoving = false; _arrived = false;
_rb.velocity = Vector2.zero; _rb.velocity = Vector2.zero;
} }
public bool IsAtDestination()
{
if (!_destination.HasValue) return true;
return ((Vector2)transform.position - _destination.Value).sqrMagnitude
<= _stoppingDist * _stoppingDist;
}
public void SetSpeed(float speed) => _moveSpeed = speed; public void SetSpeed(float speed) => _moveSpeed = speed;
public bool IsNearEdge() => false; // 飞行单位不检测平台边缘 public bool TryPickWanderPoint(out Vector2 point)
public bool CanReach(Vector2 target) => true; // 飞行单位直飞,始终可达
public bool WalkToRandom()
{ {
// 在当前位置随机偏移一个 2D 方向(供 BD_WalkRandom 调用) // 在当前位置随机偏移一个 2D 方向,供上层游荡状态调用;不主动发起移动。
Vector2 offset = UnityEngine.Random.insideUnitCircle.normalized * _randomWalkRadius; Vector2 offset = Random.insideUnitCircle.normalized * _randomWalkRadius;
RequestMoveTo((Vector2)transform.position + offset); point = (Vector2)transform.position + offset;
return true;
}
// 飞行单位无"导航段"概念,其 WalkToRandom 本就是当前位置的有界局部偏移,
// 天然满足"就近游走"语义,直接复用。
public bool WalkToRandomOnSegment() => WalkToRandom();
// 飞行单位直飞、无地面/崖沿/身体宽度约束——任意目标本就"站得住",原样返回即可。
public bool ResolveStandablePoint(Vector2 target, out Vector2 standable)
{
standable = target;
return true; return true;
} }
@@ -138,19 +108,13 @@ namespace BaseGames.Enemies.Navigation
private void UpdateChase() private void UpdateChase()
{ {
Vector2 myPos = _rb.position; Vector2 myPos = _rb.position;
Vector2 target = _destination.Value; Vector2 target = _target;
float sqrDist = (target - myPos).sqrMagnitude; float sqrDist = (target - myPos).sqrMagnitude;
if (sqrDist <= _stoppingDist * _stoppingDist) if (sqrDist <= _stoppingDist * _stoppingDist)
{ {
_rb.velocity = Vector2.zero; _rb.velocity = Vector2.zero;
_isMoving = false; _arrived = true;
_destination = null;
if (!_goalFired)
{
_goalFired = true;
OnGoalReached?.Invoke();
}
return; return;
} }