Files
zeling_v2/Assets/_Game/Scripts/Editor/Modules/StreamingModule.cs
Joywayer 6eaa83dc71 feat: Round 48 narrative systems improvements
- QuestSO: Add ValidateBranchCycles() DFS detection for branches[].nextQuest loop
- QuestSO: Mark three legacy prerequisite fields with v2.0 removal warning in Tooltip
- IQuestManager: Add QuestLockReason enum + QuestLockInfo struct (strongly-typed lock info)
- IQuestManager: Add GetQuestLockInfo() method to interface; GetQuestLockReason() now delegates to it
- IQuestEventSource: Add OnQuestStateChanged(questId, oldState, newState) unified event
- QuestManager: Implement GetQuestLockInfo(); fire OnQuestStateChanged on all state transitions
- DialogueManager: Add one-frame yield in HandleChoices before ShowChoices (skip-debounce fix)
- DialogueManager: Increment _playbackId in ForceEnd() to invalidate residual choice callbacks
- DialogueSequenceSO: Add UNITY_EDITOR debug log in TryGetActiveVariant on variant match
- WorldStateRegistry: Add OnBatchStateChanged event + BatchMark() batch-write API
- DialogueModule: List badge shows warning indicator for unconditional-shadowing variants
- DialogueModule: BuildVariantsCard shows logic mode (AND/OR) alongside flag conditions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 00:05:15 +08:00

157 lines
6.3 KiB
C#

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, IDataModuleOrdered
{
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";
public int DisplayOrder => 70;
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;
}
}
}