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

@@ -12,6 +12,8 @@ using BaseGames.UI.MainMenu;
using BaseGames.UI.Menus;
using BaseGames.UI.Splash;
using BaseGames.World;
using BaseGames.World.Map;
using BaseGames.World.Streaming;
using PathBerserker2d;
using Unity.Cinemachine;
using UnityEditor;
@@ -230,6 +232,9 @@ namespace BaseGames.Editor
AddScaffoldNote(hudRootGo, "HUDController 已挂载。其内部图片/文本/图标 Prefab 依赖较多,需后续手工补 UI 资源与事件频道。", report);
// ── 流式加载系统 ──────────────────────────────────────────────────
ScaffoldStreamingSystem(services, report);
MarkDirtyAndLog("Persistent 场景脚手架", root, report);
}
@@ -392,6 +397,79 @@ namespace BaseGames.Editor
MarkDirtyAndLog("Game Room 脚手架", root, report);
}
// ─────────────────────────────────────────────────────────────────────
// 流式加载系统RoomStreamingManager + TransitionDirector
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// 在 [Services] 下创建或更新 SYS_RoomStreamingManager
/// 挂载 <see cref="RoomStreamingManager"/> 与 <see cref="TransitionDirector"/>
/// 并自动绑定已存在的事件频道与配置资产。
/// </summary>
private static void ScaffoldStreamingSystem(Transform services, List<string> report)
{
// 预算配置 SO不存在时自动创建
StreamingBudgetConfigSO budgetConfig = EnsureStreamingBudgetConfigAsset(report);
// MapDatabaseSO查找已存在的资产
Object mapDbAsset = FindFirstAssetByType<MapDatabaseSO>("MapDatabase", "MAP_Database", "MapDatabaseSO");
if (mapDbAsset == null)
report.Add("未找到 MapDatabaseSO 资产。请将 MapDatabaseSO 手工赋给 RoomStreamingManager._mapDatabase 与 TransitionDirector._mapDatabase。");
// ── SYS_RoomStreamingManager GameObject ──────────────────────────
GameObject streamingGo = GetOrCreateChild(services, "SYS_RoomStreamingManager").gameObject;
RoomStreamingManager streamingMgr = GetOrAddComponent<RoomStreamingManager>(streamingGo);
TransitionDirector transitionDir = GetOrAddComponent<TransitionDirector>(streamingGo);
// ── RoomStreamingManager 字段 ─────────────────────────────────────
AssignReference(streamingMgr, "_mapDatabase", mapDbAsset);
AssignReference(streamingMgr, "_budget", budgetConfig);
AssignAsset(streamingMgr, "_onRoomEntered", report, false, "EVT_RoomEntered");
AssignAsset(streamingMgr, "_onRoomPreloaded", report, false, "EVT_RoomPreloaded");
// ── TransitionDirector 字段 ───────────────────────────────────────
AssignReference(transitionDir, "_streamingManager", streamingMgr);
AssignReference(transitionDir, "_mapDatabase", mapDbAsset);
AssignReference(transitionDir, "_budget", budgetConfig);
AssignAsset(transitionDir, "_onFadeOutRequest", report, false, "EVT_FadeOutRequest");
AssignAsset(transitionDir, "_onFadeInRequest", report, false, "EVT_FadeInRequest");
AssignAsset(transitionDir, "_onRegionNameDisplay", report, false, "EVT_RegionNameDisplay");
AssignAsset(transitionDir, "_onSceneWorldStateRestored", report, false, "EVT_SceneWorldStateRestored");
report.Add("SYS_RoomStreamingManager流式加载系统已创建。如 EVT_RoomEntered / EVT_RoomPreloaded 频道尚未存在,请通过 DataHub > Streaming 创建后重新运行脚手架。");
}
/// <summary>
/// 在 <c>Assets/_Game/Data/Streaming/</c> 下确保默认预算配置 SO 存在。
/// 已存在时直接返回;不存在时自动创建 <c>STR_BudgetConfig_Default.asset</c>。
/// </summary>
private static StreamingBudgetConfigSO EnsureStreamingBudgetConfigAsset(List<string> report)
{
// 先查找已有资产
string[] guids = AssetDatabase.FindAssets("t:StreamingBudgetConfigSO");
if (guids != null && guids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
var existing = AssetDatabase.LoadAssetAtPath<StreamingBudgetConfigSO>(path);
if (existing != null)
return existing;
}
// 没有则创建默认资产
const string folder = "Assets/_Game/Data/Streaming";
const string assetPath = folder + "/STR_BudgetConfig_Default.asset";
EnsureFolder(folder);
var created = ScriptableObject.CreateInstance<StreamingBudgetConfigSO>();
AssetDatabase.CreateAsset(created, assetPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
report.Add($"已自动创建流式加载预算配置:{assetPath}。可在 DataHub > 流式加载 中编辑默认参数。");
return created;
}
private static void AssignString(Object target, string propertyName, string value, List<string> report = null)
{
SerializedObject serializedObject = new SerializedObject(target);