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:
155
Assets/_Game/Scripts/Editor/Modules/StreamingModule.cs
Normal file
155
Assets/_Game/Scripts/Editor/Modules/StreamingModule.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using BaseGames.World.Streaming;
|
||||
|
||||
namespace BaseGames.Editor.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// DataHub 流式加载模块 —— 管理 <see cref="StreamingBudgetConfigSO"/> 资产。
|
||||
/// </summary>
|
||||
public class StreamingModule : IDataModule
|
||||
{
|
||||
private const string Folder = "Assets/_Game/Data/Streaming";
|
||||
private const string Prefix = "STR_";
|
||||
|
||||
public string ModuleId => "streaming";
|
||||
public string DisplayName => "流式加载";
|
||||
public string IconName => "d_RectTransformBlueprint";
|
||||
|
||||
private SoListPane<StreamingBudgetConfigSO> _listPane;
|
||||
private DetailHeader _header;
|
||||
private StreamingBudgetConfigSO _selected;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_listPane = new SoListPane<StreamingBudgetConfigSO>(
|
||||
Folder, Prefix,
|
||||
cfg => $"休眠上限 {cfg.MaxDormantRooms} 预加载深度 {cfg.PreloadLookaheadHops}跳");
|
||||
_listPane.SelectionChanged = sel =>
|
||||
{
|
||||
_selected = sel;
|
||||
};
|
||||
}
|
||||
|
||||
public void BuildListPane(VisualElement container, Action<UnityEngine.Object> onSelected)
|
||||
{
|
||||
_listPane.SelectionChanged = sel =>
|
||||
{
|
||||
_selected = sel;
|
||||
onSelected?.Invoke(sel);
|
||||
};
|
||||
|
||||
// 顶部操作栏(新建)
|
||||
var topBar = new VisualElement();
|
||||
topBar.style.flexDirection = FlexDirection.Row;
|
||||
topBar.style.paddingLeft = 8;
|
||||
topBar.style.paddingRight = 8;
|
||||
topBar.style.paddingTop = 6;
|
||||
topBar.style.paddingBottom = 6;
|
||||
topBar.style.borderBottomWidth = 1;
|
||||
topBar.style.borderBottomColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.3f));
|
||||
container.Add(topBar);
|
||||
|
||||
var createBtn = new Button(() =>
|
||||
{
|
||||
var created = AssetOperations.Create<StreamingBudgetConfigSO>(Folder, "STR_BudgetConfig_New");
|
||||
if (created != null) _listPane.Refresh(created);
|
||||
}) { text = "+ 新建配置" };
|
||||
createBtn.style.flexGrow = 1;
|
||||
topBar.Add(createBtn);
|
||||
|
||||
container.Add(_listPane);
|
||||
_listPane.style.flexGrow = 1;
|
||||
_listPane.Refresh();
|
||||
}
|
||||
|
||||
public void BuildDetailPane(VisualElement container, UnityEngine.Object selected)
|
||||
{
|
||||
_selected = selected as StreamingBudgetConfigSO;
|
||||
|
||||
_header = new DetailHeader();
|
||||
_header.SetAsset(_selected);
|
||||
_header.RenameRequested += newName => OnRenameRequested(selected, newName);
|
||||
container.Add(_header);
|
||||
|
||||
if (_selected == null) return;
|
||||
|
||||
container.Add(BuildStatsCard(_selected));
|
||||
container.Add(BuildActionBar(_selected));
|
||||
container.Add(SkillModule.MakeDivider());
|
||||
container.Add(new InspectorElement(_selected));
|
||||
}
|
||||
|
||||
public void OnActivated() => _listPane?.Refresh();
|
||||
|
||||
// ── Stats Card ────────────────────────────────────────────────────────
|
||||
|
||||
private static VisualElement BuildStatsCard(StreamingBudgetConfigSO cfg)
|
||||
{
|
||||
var card = SkillModule.MakeCard();
|
||||
SkillModule.AddChip(card, "最大休眠房间", $"{cfg.MaxDormantRooms}");
|
||||
SkillModule.AddChip(card, "内存上限", $"{cfg.MaxMemoryMB} MB");
|
||||
SkillModule.AddChip(card, "并发加载数", $"{cfg.MaxConcurrentLoads}");
|
||||
SkillModule.AddChip(card, "预加载跳数", $"{cfg.PreloadLookaheadHops}");
|
||||
SkillModule.AddChip(card, "冷却时长", $"{cfg.CoolingDuration:F1}s");
|
||||
SkillModule.AddChip(card, "每帧激活数", $"{cfg.LifecycleActivatePerFrame}");
|
||||
return card;
|
||||
}
|
||||
|
||||
// ── Action Bar ────────────────────────────────────────────────────────
|
||||
|
||||
private VisualElement BuildActionBar(StreamingBudgetConfigSO cfg)
|
||||
{
|
||||
var bar = SkillModule.MakeActionBar();
|
||||
|
||||
new Button(() =>
|
||||
{
|
||||
EditorGUIUtility.PingObject(cfg);
|
||||
Selection.activeObject = cfg;
|
||||
}) { text = "定位" }.AlsoAddTo(bar);
|
||||
|
||||
new Button(() =>
|
||||
{
|
||||
var c = AssetOperations.Clone(cfg, Folder);
|
||||
if (c != null) _listPane.Refresh(c);
|
||||
}) { text = "克隆..." }.AlsoAddTo(bar);
|
||||
|
||||
var del = new Button(() =>
|
||||
{
|
||||
if (AssetOperations.Delete(cfg)) _listPane.Refresh(null);
|
||||
}) { text = "删除" };
|
||||
ApplyDeleteStyle(del);
|
||||
del.AlsoAddTo(bar);
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
// ── 重命名 ────────────────────────────────────────────────────────────
|
||||
|
||||
private void OnRenameRequested(UnityEngine.Object asset, string newName)
|
||||
{
|
||||
var (ok, err) = AssetOperations.Rename(asset, newName);
|
||||
if (!ok) EditorUtility.DisplayDialog("重命名失败", err, "确定");
|
||||
else { _header.SetAsset(asset); _listPane.Invalidate(); }
|
||||
}
|
||||
|
||||
// ── 共用 ─────────────────────────────────────────────────────────────
|
||||
|
||||
private static void ApplyDeleteStyle(Button btn)
|
||||
{
|
||||
var c = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f));
|
||||
btn.style.borderLeftColor = c;
|
||||
btn.style.borderRightColor = c;
|
||||
btn.style.borderTopColor = c;
|
||||
btn.style.borderBottomColor = c;
|
||||
btn.style.borderLeftWidth = 1;
|
||||
btn.style.borderRightWidth = 1;
|
||||
btn.style.borderTopWidth = 1;
|
||||
btn.style.borderBottomWidth = 1;
|
||||
btn.style.marginLeft = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user