using System; using UnityEngine; using BaseGames.Enemies; namespace BaseGames.Enemies.Navigation { /// /// 飞行单位直线导航代理。 /// 不依赖 PathBerserker2d 寻路,直接通过 Rigidbody2D.MovePosition 向目标点直飞。 /// /// 特性: /// /// 空闲时施加正弦波形悬停偏移,产生飘浮感。 /// NavLink 相关成员均返回安全默认值(飞行单位不使用平台连接段)。 /// 目标到达后触发 事件(替代轮询 IsAtDestination)。 /// /// /// 使用方式:挂载到飞行怪 Prefab 根节点,替代 EnemyNavAgent; /// 的 GetComponent<IPathAgent>() 自动发现此组件。 /// [RequireComponent(typeof(Rigidbody2D))] public sealed class FlyingDirectNavigator : MonoBehaviour, IPathAgent { [Header("移动参数")] [Tooltip("直飞速度(m/s)")] [SerializeField] private float _moveSpeed = 3f; [Tooltip("到达目标的判定距离(m)")] [SerializeField] private float _stoppingDist = 0.15f; [Header("游荡参数(WalkToRandom)")] [Tooltip("随机游荡目标点距当前位置的最大半径(m)。")] [Min(0.1f)] [SerializeField] private float _randomWalkRadius = 3f; [Header("悬停参数(空闲 / 无目标时)")] [Tooltip("悬停横向摆动速度(m/s)")] [SerializeField] private float _hoverLateralSpeed = 0.5f; [Tooltip("悬停纵向正弦频率(Hz)")] [SerializeField] private float _hoverSineFrequency = 1.2f; [Tooltip("悬停纵向正弦振幅(m/s)")] [SerializeField] private float _hoverSineAmplitude = 0.25f; [Tooltip("横向方向翻转周期(s)")] [SerializeField] private float _hoverFlipInterval = 1.5f; // ── IPathAgent 事件 ──────────────────────────────────────────── public event Action OnLinkStarted { add { } remove { } } public event Action OnLinkCompleted { add { } remove { } } public event Action OnNavPathFailed { add { } remove { } } public event Action OnGoalReached; // ── 状态 ─────────────────────────────────────────────────────── private Rigidbody2D _rb; private EnemyMovement _movement; private Vector2? _destination; private bool _isMoving; private bool _goalFired; private float _hoverTimer; private float _hoverFlipTimer; private float _hoverDir = 1f; // ── IPathAgent 属性 ──────────────────────────────────────────── public bool IsMoving => _isMoving; public bool IsOnLink => false; public NavLinkType CurrentLinkType => NavLinkType.None; public Vector2 CurrentLinkStart => Vector2.zero; public Vector2 CurrentLinkEnd => Vector2.zero; // ── Unity 生命周期 ───────────────────────────────────────────── private void Awake() { _rb = GetComponent(); _movement = GetComponent(); _rb.gravityScale = 0f; _rb.constraints = RigidbodyConstraints2D.FreezeRotation; } private void FixedUpdate() { if (_destination.HasValue) UpdateChase(); else UpdateHover(); } // ── IPathAgent 方法 ──────────────────────────────────────────── public void RequestMoveTo(Vector2 target) { _destination = target; _isMoving = true; _goalFired = false; } public void StopNavigation() { _destination = null; _isMoving = false; _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 bool IsNearEdge() => false; // 飞行单位不检测平台边缘 public bool CanReach(Vector2 target) => true; // 飞行单位直飞,始终可达 public bool WalkToRandom() { // 在当前位置随机偏移一个 2D 方向(供 BD_WalkRandom 调用) Vector2 offset = UnityEngine.Random.insideUnitCircle.normalized * _randomWalkRadius; RequestMoveTo((Vector2)transform.position + offset); return true; } // ── 内部移动逻辑 ─────────────────────────────────────────────── private void UpdateChase() { Vector2 myPos = _rb.position; Vector2 target = _destination.Value; float sqrDist = (target - myPos).sqrMagnitude; if (sqrDist <= _stoppingDist * _stoppingDist) { _rb.velocity = Vector2.zero; _isMoving = false; _destination = null; if (!_goalFired) { _goalFired = true; OnGoalReached?.Invoke(); } return; } Vector2 newPos = Vector2.MoveTowards(myPos, target, _moveSpeed * Time.fixedDeltaTime); _rb.MovePosition(newPos); // 面向移动方向(通过 EnemyMovement 输入信号,保持 _facingDir 与动画系统同步) float dx = target.x - myPos.x; if (Mathf.Abs(dx) > 0.05f) { int dir = dx > 0f ? 1 : -1; if (_movement != null) { _movement.PendingInput.WantFace = true; _movement.PendingInput.FaceDir = dir; } else { // 降级:没有 EnemyMovement 时直接翻转(独立飞行单位) var s = transform.localScale; s.x = Mathf.Abs(s.x) * dir; transform.localScale = s; } } } private void UpdateHover() { _hoverFlipTimer += Time.fixedDeltaTime; if (_hoverFlipTimer >= _hoverFlipInterval) { _hoverFlipTimer = 0f; _hoverDir = -_hoverDir; } float sineY = Mathf.Sin(Time.time * _hoverSineFrequency * Mathf.PI * 2f) * _hoverSineAmplitude; _rb.velocity = new Vector2(_hoverDir * _hoverLateralSpeed, sineY); } } }