feat: Implement Room Streaming System
- Add RoomStreamingManager to manage room loading and unloading based on player proximity. - Create StreamingBudgetConfigSO for memory and performance budgeting of the streaming system. - Introduce TransitionDirector to handle seamless and atmospheric fade transitions between rooms. - Develop WorldGraph to represent room connectivity and facilitate neighbor queries and distance calculations. - Implement RoomNode and RoomEdge classes to structure room data and connections.
This commit is contained in:
165
Assets/_Game/Scripts/Enemies/Navigation/FlyingDirectNavigator.cs
Normal file
165
Assets/_Game/Scripts/Enemies/Navigation/FlyingDirectNavigator.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Enemies.Navigation
|
||||
{
|
||||
/// <summary>
|
||||
/// 飞行单位直线导航代理。
|
||||
/// 不依赖 PathBerserker2d 寻路,直接通过 Rigidbody2D.MovePosition 向目标点直飞。
|
||||
///
|
||||
/// 特性:
|
||||
/// <list type="bullet">
|
||||
/// <item>空闲时施加正弦波形悬停偏移,产生飘浮感。</item>
|
||||
/// <item>NavLink 相关成员均返回安全默认值(飞行单位不使用平台连接段)。</item>
|
||||
/// <item>目标到达后触发 <see cref="OnGoalReached"/> 事件(替代轮询 IsAtDestination)。</item>
|
||||
/// </list>
|
||||
///
|
||||
/// 使用方式:挂载到飞行怪 Prefab 根节点,替代 EnemyNavAgent;
|
||||
/// <see cref="EnemyBase.Awake"/> 的 GetComponent<IPathAgent>() 自动发现此组件。
|
||||
/// </summary>
|
||||
[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<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 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<Rigidbody2D>();
|
||||
_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);
|
||||
|
||||
// 面向移动方向
|
||||
float dx = target.x - myPos.x;
|
||||
if (Mathf.Abs(dx) > 0.05f)
|
||||
{
|
||||
var s = transform.localScale;
|
||||
s.x = Mathf.Abs(s.x) * Mathf.Sign(dx);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user