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:
2026-05-23 19:10:29 +08:00
parent 81c326af53
commit a1b4e629aa
165 changed files with 7904 additions and 313 deletions

View File

@@ -0,0 +1,96 @@
#if GRAPH_DESIGNER
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
using UnityEngine;
namespace BaseGames.Enemies.AI
{
/// <summary>
/// BD Action战斗站位控制。
/// 在 [m_PreferredMinDist, m_PreferredMaxDist] 范围内保持与玩家的水平距离:
/// - 距离过近 → 后退
/// - 距离过远 → 靠近
/// - 在范围内 → 停止移动,持续朝向玩家
/// 始终返回 Running上层 BT 通过 Abort 或条件终止该 Task。
/// </summary>
[TaskName("Maintain Combat Distance")]
[TaskCategory("BaseGames/Enemy/Combat")]
[TaskDescription("在战斗中维持与目标的理想距离范围")]
public class BD_MaintainCombatDistance : Action
{
[Tooltip("期望最小距离m小于此值开始后退")]
[SerializeField] private float m_PreferredMinDist = 2f;
[Tooltip("期望最大距离m大于此值开始靠近")]
[SerializeField] private float m_PreferredMaxDist = 4f;
[Tooltip("后退速度m/s默认使用 WalkSpeed")]
[SerializeField] private float m_BackpedaleSpeed = 0f;
[Tooltip("每帧重新规划路径的阈值m玩家移动超过此距离才重规划降低频率")]
[SerializeField] private float m_ReplanThreshold = 0.5f;
private EnemyBase _enemy;
private Vector2 _lastPlayerPos;
private float _sqrMin;
private float _sqrMax;
public override void OnAwake() => _enemy = GetComponent<EnemyBase>();
public override void OnStart()
{
_sqrMin = m_PreferredMinDist * m_PreferredMinDist;
_sqrMax = m_PreferredMaxDist * m_PreferredMaxDist;
_lastPlayerPos = Vector2.positiveInfinity;
}
public override TaskStatus OnUpdate()
{
if (_enemy == null || _enemy.PlayerTransform == null)
return TaskStatus.Failure;
if (_enemy.Stats == null)
return TaskStatus.Failure;
_enemy.FacePlayer();
float sqrDist = _enemy.Stats.SqrDistanceToPlayer;
if (sqrDist < _sqrMin)
{
// 距离过近 → 后退(向远离玩家方向移动)
_enemy.Nav?.StopNavigation();
Vector2 toPlayer = ((Vector2)_enemy.PlayerTransform.position - (Vector2)_enemy.transform.position).normalized;
float backDir = -Mathf.Sign(toPlayer.x);
float speed = m_BackpedaleSpeed > 0f ? m_BackpedaleSpeed : _enemy.Stats.WalkSpeed;
_enemy.Movement?.MoveWithSpeed(backDir, speed);
}
else if (sqrDist > _sqrMax)
{
// 距离过远 → 靠近
Vector2 playerPos = _enemy.PlayerTransform.position;
float moved = ((Vector2)playerPos - _lastPlayerPos).sqrMagnitude;
if (moved > m_ReplanThreshold * m_ReplanThreshold)
{
_lastPlayerPos = playerPos;
_enemy.MoveTo(playerPos);
}
}
else
{
// 在最优范围内 → 停止导航,原地保持朝向
_enemy.Nav?.StopNavigation();
_enemy.Movement?.StopHorizontal();
}
return TaskStatus.Running;
}
public override void OnEnd()
{
_enemy?.Movement?.StopHorizontal();
_enemy?.Nav?.StopNavigation();
}
}
}
#endif