Files
zeling_v2/Assets/_Game/Scripts/Editor/Hub/DataHubWindow.cs
Joywayer 446fd5dcd0 feat: Add WorldStateFlagAttribute and custom property drawer for enhanced dialogue management
- Implemented WorldStateFlagAttribute to mark string fields as world state flags.
- Created NarrativeNPCEditor for custom inspector to visualize dialogue version activation states.
- Developed WorldStateFlagDrawer to provide dropdown menu for known flags in the inspector.
- Introduced ActorModule for managing DialogueActorSO assets, including viewing, creating, and deleting actors.
- Added DialogueModule for managing DialogueSequenceSO assets with detailed previews and action bars.
- Established QuestModule for managing QuestSO assets, including objectives and branches.
- Implemented QuestManagerPostprocessor to automatically refresh QuestManager's quest list on asset changes.
2026-05-24 00:36:11 +08:00

279 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using BaseGames.Editor.Modules;
namespace BaseGames.Editor
{
/// <summary>
/// 数据管理总枢纽窗口DataHub
/// 布局导航侧边栏120px | TwoPaneSplitView → 列表区220px + 详情区flex
/// 菜单BaseGames / Data Hub (priority=50)
/// </summary>
public class DataHubWindow : EditorWindow
{
private const string UssPath = "Assets/_Game/Scripts/Editor/UIToolkit/Editor.uss";
private const string PrefKey = "DataHub.ActiveModuleId";
private const float NavWidth = 120f;
private const float ListWidth = 220f;
private const float MinWinWidth = 680f;
private const float MinWinHeight = 420f;
[MenuItem("BaseGames/Data Hub", priority = 50)]
public static void Open()
{
var wnd = GetWindow<DataHubWindow>();
wnd.titleContent = new GUIContent("Data Hub", EditorGUIUtility.IconContent("d_ScriptableObject Icon").image);
wnd.minSize = new Vector2(MinWinWidth, MinWinHeight);
}
// ── 状态 ─────────────────────────────────────────────────────────────
private readonly List<IDataModule> _modules = new();
private readonly HashSet<string> _initializedIds = new();
private IDataModule _activeModule;
private VisualElement _navSidebar;
// 缓存:列表区和详情区引用(由 TwoPaneSplitView 子节点提供)
private VisualElement _listWrapper;
private VisualElement _detailWrapper;
// 当前选中资产
private UnityEngine.Object _selected;
// ── 生命周期 ──────────────────────────────────────────────────────────
public void CreateGUI()
{
// USS
var uss = AssetDatabase.LoadAssetAtPath<StyleSheet>(UssPath);
if (uss != null) rootVisualElement.styleSheets.Add(uss);
// 注册模块
RegisterModules();
// 构建 UI
BuildLayout();
// 恢复上次激活的模块
string savedId = EditorPrefs.GetString(PrefKey, string.Empty);
var toActivate = _modules.Find(m => m.ModuleId == savedId) ?? _modules.FirstOrDefault();
if (toActivate != null) ActivateModule(toActivate);
}
// ── 模块注册 ──────────────────────────────────────────────────────────
private void RegisterModules()
{
_modules.Clear();
_modules.Add(new WeaponModule());
_modules.Add(new SkillModule());
_modules.Add(new EnemyModule());
_modules.Add(new FormModule());
_modules.Add(new BossSkillModule());
_modules.Add(new CharmModule());
_modules.Add(new StreamingModule());
_modules.Add(new DialogueModule());
_modules.Add(new QuestModule());
_modules.Add(new ActorModule());
}
// ── 布局 ─────────────────────────────────────────────────────────────
private void BuildLayout()
{
var root = rootVisualElement;
root.style.flexDirection = FlexDirection.Row;
root.style.flexGrow = 1;
// 导航侧边栏
_navSidebar = BuildNavSidebar();
root.Add(_navSidebar);
// 垂直分隔线
var divider = new VisualElement();
divider.style.width = 1;
divider.style.backgroundColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.25f));
root.Add(divider);
// TwoPaneSplitView列表 + 详情)
var split = new TwoPaneSplitView(0, ListWidth, TwoPaneSplitViewOrientation.Horizontal);
split.style.flexGrow = 1;
root.Add(split);
// 列表区容器
_listWrapper = new VisualElement();
_listWrapper.style.flexGrow = 1;
_listWrapper.style.overflow = Overflow.Hidden;
split.Add(_listWrapper);
// 详情区:外层 ScrollView 提供滚动视口_detailWrapper 是内容容器(自然高度)
var detailScroll = new ScrollView(ScrollViewMode.Vertical);
detailScroll.style.flexGrow = 1;
detailScroll.style.overflow = Overflow.Hidden;
split.Add(detailScroll);
_detailWrapper = new VisualElement();
_detailWrapper.style.flexDirection = FlexDirection.Column;
_detailWrapper.style.paddingBottom = 16;
detailScroll.Add(_detailWrapper);
}
private VisualElement BuildNavSidebar()
{
var sidebar = new VisualElement();
sidebar.style.width = NavWidth;
sidebar.style.flexShrink = 0;
sidebar.style.flexDirection = FlexDirection.Column;
sidebar.style.paddingTop = 8;
// 标题
var title = new Label("DATA HUB");
title.style.fontSize = 10;
title.style.opacity = 0.5f;
title.style.paddingLeft = 10;
title.style.marginBottom = 6;
title.style.unityFontStyleAndWeight = FontStyle.Bold;
sidebar.Add(title);
foreach (var module in _modules)
{
var btn = BuildNavItem(module);
sidebar.Add(btn);
}
// 弹性填充
var spacer = new VisualElement();
spacer.style.flexGrow = 1;
sidebar.Add(spacer);
return sidebar;
}
private Button BuildNavItem(IDataModule module)
{
var btn = new Button(() => ActivateModule(module));
btn.name = "nav-" + module.ModuleId;
btn.style.flexDirection = FlexDirection.Row;
btn.style.alignItems = Align.Center;
btn.style.paddingLeft = 10;
btn.style.paddingRight = 8;
btn.style.paddingTop = 8;
btn.style.paddingBottom = 8;
btn.style.borderTopLeftRadius = 0;
btn.style.borderTopRightRadius = 0;
btn.style.borderBottomLeftRadius = 0;
btn.style.borderBottomRightRadius = 0;
btn.style.borderLeftWidth = 0;
btn.style.borderRightWidth = 0;
btn.style.borderTopWidth = 0;
btn.style.borderBottomWidth = 0;
btn.style.backgroundColor = new StyleColor(Color.clear);
btn.style.marginBottom = 2;
// 图标(容错:图标名无效时跳过,不报错)
if (!string.IsNullOrEmpty(module.IconName))
{
var content = EditorGUIUtility.IconContent(module.IconName);
if (content?.image != null)
{
var icon = new Image { image = content.image };
icon.style.width = 16;
icon.style.height = 16;
icon.style.marginRight = 6;
btn.Add(icon);
}
}
var label = new Label(module.DisplayName);
label.style.flexGrow = 1;
btn.Add(label);
return btn;
}
// ── 模块切换 ──────────────────────────────────────────────────────────
private void ActivateModule(IDataModule module)
{
if (_activeModule == module) return;
_activeModule = module;
_selected = null;
// 更新导航项视觉状态
foreach (var m in _modules)
{
var navBtn = _navSidebar.Q<Button>("nav-" + m.ModuleId);
if (navBtn == null) continue;
if (m == module)
{
navBtn.style.backgroundColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.18f));
navBtn.style.borderLeftWidth = 3;
navBtn.style.borderLeftColor = new StyleColor(new Color(0.4f, 0.65f, 1f, 1f));
}
else
{
navBtn.style.backgroundColor = new StyleColor(Color.clear);
navBtn.style.borderLeftWidth = 0;
}
}
// 初始化模块(首次激活时调用一次)
if (!_initializedIds.Contains(module.ModuleId))
{
_initializedIds.Add(module.ModuleId);
module.Initialize();
}
module.OnActivated();
// 重建列表区
_listWrapper.Clear();
module.BuildListPane(_listWrapper, OnModuleSelected);
// 清空详情区
RebuildDetailPane(null);
EditorPrefs.SetString(PrefKey, module.ModuleId);
}
private void OnModuleSelected(UnityEngine.Object selected)
{
_selected = selected;
RebuildDetailPane(selected);
}
private void RebuildDetailPane(UnityEngine.Object selected)
{
_detailWrapper.Clear();
if (_activeModule == null) return;
if (selected == null)
{
var placeholder = new Label("← 从左侧列表选择一项");
placeholder.style.opacity = 0.45f;
placeholder.style.marginTop = 60;
placeholder.style.unityTextAlign = TextAnchor.MiddleCenter;
_detailWrapper.Add(placeholder);
return;
}
_activeModule.BuildDetailPane(_detailWrapper, selected);
}
// ── 公共辅助(供 Module 回调使用)────────────────────────────────────
/// <summary>通知 Hub 已完成重命名,需要刷新详情区标题。</summary>
public void NotifyRenamed(UnityEngine.Object asset)
{
if (_activeModule == null || asset == null) return;
RebuildDetailPane(asset);
}
}
}