feat: Add SkillModule and WeaponModule for managing skills and weapons

- Implemented SkillModule to manage FormSkillSO assets with a detailed UI for editing and displaying skill properties.
- Implemented WeaponModule to manage WeaponSO assets with a detailed UI for editing and displaying weapon properties.
- Created AssetOperations class for centralized CRUD operations on ScriptableObject assets, including create, rename, delete, and clone functionalities.
- Added DetailHeader for displaying and renaming asset names in the UI.
- Introduced SoListPane for a reusable ScriptableObject list panel with search functionality and context menus.
- Added meta files for all new scripts to ensure proper asset management in Unity.
This commit is contained in:
2026-05-21 07:09:53 +08:00
parent f096105caf
commit bb3afd130f
41 changed files with 2417 additions and 1618 deletions

View File

@@ -0,0 +1,262 @@
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());
}
// ── 布局 ─────────────────────────────────────────────────────────────
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;
split.Add(_listWrapper);
// 详情区容器
_detailWrapper = new VisualElement();
_detailWrapper.style.flexGrow = 1;
split.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 icon = new Image { image = EditorGUIUtility.IconContent(module.IconName).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);
}
}
}