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,89 @@
using System.Collections.Generic;
using UnityEngine;
using Micosmo.SensorToolkit;
namespace BaseGames.Enemies.Perception
{
/// <summary>
/// 敌人感知 Hub架构 07_EnemyModule §9
/// 集中暴露挂载在敌人 Prefab 上的各种 SensorToolkit SensorBD 任务通过
/// 字符串槽位查询,避免在 BD 任务 Inspector 中拖具体 Sensor 引用。
///
/// 典型槽位命名约定:
/// - "aggro" : RangeSensor2D玩家入侵警戒圈
/// - "attack_melee" : RangeSensor2D近战触发距离
/// - "attack_range" : RangeSensor2D远程触发距离
/// - "los" : LOSSensor2D视线
/// - "wall_ahead" : RaySensor2D前方墙体检测
/// - "ledge" : RaySensor2D前方悬崖检测
/// </summary>
[DisallowMultipleComponent]
public sealed class EnemySensorHub : MonoBehaviour, IPerceptionSystem
{
[System.Serializable]
public struct SensorSlot
{
public string slotName;
public Sensor sensor;
}
[SerializeField] private SensorSlot[] _slots;
private Dictionary<string, Sensor> _map;
private void Awake()
{
_map = new Dictionary<string, Sensor>(_slots?.Length ?? 0);
if (_slots == null) return;
for (int i = 0; i < _slots.Length; i++)
{
var s = _slots[i];
if (s.sensor != null && !string.IsNullOrEmpty(s.slotName))
_map[s.slotName] = s.sensor;
}
}
public Sensor Get(string slotName)
{
if (_map == null || string.IsNullOrEmpty(slotName)) return null;
_map.TryGetValue(slotName, out var s);
return s;
}
public bool IsDetecting(string slotName, GameObject target)
{
var s = Get(slotName);
return s != null && target != null && s.IsDetected(target);
}
public bool HasAnyDetection(string slotName)
{
var s = Get(slotName);
if (s == null) return false;
foreach (var _ in s.Detections) return true;
return false;
}
public GameObject GetFirstDetection(string slotName)
{
var s = Get(slotName);
if (s == null) return null;
foreach (var go in s.Detections) return go;
return null;
}
/// <summary>
/// 暂停或恢复所有插槽的 Sensor。
/// 当敌人超出 QuotaManager 活跃范围时调用(关闭),归入活跃范围时恢复(开启)。
/// </summary>
public void SetSuspended(bool suspended)
{
if (_slots == null) return;
for (int i = 0; i < _slots.Length; i++)
{
var sensor = _slots[i].sensor;
if (sensor != null) sensor.enabled = !suspended;
}
}
}
}