Files
zeling_v2/Assets/_Game/Scripts/Enemies/Navigation/FlyingNavigator.cs
T

158 lines
6.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using BaseGames.Enemies;
namespace BaseGames.Enemies.Navigation
{
/// <summary>
/// 飞行单位直线导航代理,实现 <see cref="IEnemyNavigator"/>。
/// 不依赖寻路网格,直接通过 Rigidbody2D.MovePosition 向目标点直飞。
///
/// 特性:
/// <list type="bullet">
/// <item>空闲时施加正弦波形悬停偏移,产生飘浮感。</item>
/// <item>飞行单位直飞,无地形阻挡概念,<see cref="IsBlocked"/> 恒为 false。</item>
/// </list>
///
/// 使用方式:挂载到飞行怪 Prefab 根节点,替代 EnemyNavAgent
/// <see cref="EnemyBase.Awake"/> 的 GetComponent&lt;IEnemyNavigator&gt;() 自动发现此组件。
/// </summary>
[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<Rigidbody2D>();
_movement = GetComponent<EnemyMovement>();
_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);
}
}
}