using UnityEngine;
using BaseGames.Enemies;
namespace BaseGames.Enemies.Navigation
{
///
/// 飞行单位直线导航代理,实现 。
/// 不依赖寻路网格,直接通过 Rigidbody2D.MovePosition 向目标点直飞。
///
/// 特性:
///
/// - 空闲时施加正弦波形悬停偏移,产生飘浮感。
/// - 飞行单位直飞,无地形阻挡概念, 恒为 false。
///
///
/// 使用方式:挂载到飞行怪 Prefab 根节点,替代 EnemyNavAgent;
/// 的 GetComponent<IEnemyNavigator>() 自动发现此组件。
///
[RequireComponent(typeof(Rigidbody2D))]
public sealed class FlyingNavigator : MonoBehaviour, IEnemyNavigator
{
[Header("移动参数")]
[Tooltip("直飞速度(m/s)")]
[SerializeField] private float _moveSpeed = 3f;
[Tooltip("到达目标的判定距离(m)")]
[SerializeField] private float _stoppingDist = 0.15f;
[Header("游荡参数(TryPickWanderPoint)")]
[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;
// ── 状态 ───────────────────────────────────────────────────────
private Rigidbody2D _rb;
private EnemyMovement _movement;
private Vector2 _target;
private bool _hasTarget;
private bool _arrived;
private float _hoverTimer;
private float _hoverFlipTimer;
private float _hoverDir = 1f;
// ── IEnemyNavigator 属性 ────────────────────────────────────────
public bool IsMoving => _hasTarget && !_arrived;
public bool HasArrived => _arrived;
// 飞行单位直飞,无地形阻挡概念。
public bool IsBlocked => false;
// ── Unity 生命周期 ─────────────────────────────────────────────
private void Awake()
{
_rb = GetComponent();
_movement = GetComponent();
_rb.gravityScale = 0f;
_rb.constraints = RigidbodyConstraints2D.FreezeRotation;
}
private void FixedUpdate()
{
if (_hasTarget && !_arrived)
UpdateChase();
else
UpdateHover();
}
// ── IEnemyNavigator 方法 ────────────────────────────────────────
public void MoveTowards(Vector2 target)
{
// 幂等:同目标重复调用不重置状态(避免每帧重发目标导致已到达状态被反复清空)。
if (_hasTarget && (target - _target).sqrMagnitude <= 1e-6f)
return;
_target = target;
_hasTarget = true;
_arrived = false;
}
public void Stop()
{
_hasTarget = false;
_arrived = false;
_rb.velocity = Vector2.zero;
}
public void SetSpeed(float speed) => _moveSpeed = speed;
public bool TryPickWanderPoint(out Vector2 point)
{
// 在当前位置随机偏移一个 2D 方向,供上层游荡状态调用;不主动发起移动。
Vector2 offset = Random.insideUnitCircle.normalized * _randomWalkRadius;
point = (Vector2)transform.position + offset;
return true;
}
// ── 内部移动逻辑 ───────────────────────────────────────────────
private void UpdateChase()
{
Vector2 myPos = _rb.position;
Vector2 target = _target;
float sqrDist = (target - myPos).sqrMagnitude;
if (sqrDist <= _stoppingDist * _stoppingDist)
{
_rb.velocity = Vector2.zero;
_arrived = true;
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);
}
}
}