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:
212
Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs
Normal file
212
Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using BaseGames.Boss;
|
||||
|
||||
namespace BaseGames.Editor.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// DataHub Boss技能模块 —— Tab 切换管理 BossSkillSO 和 SkillSequenceSO。
|
||||
/// </summary>
|
||||
public class BossSkillModule : IDataModule
|
||||
{
|
||||
private const string SkillFolder = "Assets/_Game/Data/Boss/Skills";
|
||||
private const string SeqFolder = "Assets/_Game/Data/Boss/Sequences";
|
||||
|
||||
public string ModuleId => "boss";
|
||||
public string DisplayName => "Boss技能";
|
||||
public string IconName => "d_SkinnedMeshRenderer Icon";
|
||||
|
||||
private int _activeTab = 0;
|
||||
|
||||
private SoListPane<BossSkillSO> _skillPane;
|
||||
private SoListPane<SkillSequenceSO> _seqPane;
|
||||
private Action<UnityEngine.Object> _onSelected;
|
||||
|
||||
private DetailHeader _header;
|
||||
private BossSkillSO _selectedSkill;
|
||||
private SkillSequenceSO _selectedSeq;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_skillPane = new SoListPane<BossSkillSO>(
|
||||
SkillFolder, "ABL_Boss_",
|
||||
s => s.category.ToString());
|
||||
_skillPane.SelectionChanged = s => { _selectedSkill = s; _onSelected?.Invoke(s); };
|
||||
|
||||
_seqPane = new SoListPane<SkillSequenceSO>(SeqFolder, "ABL_Seq_");
|
||||
_seqPane.SelectionChanged = s => { _selectedSeq = s; _onSelected?.Invoke(s); };
|
||||
}
|
||||
|
||||
public void BuildListPane(VisualElement container, Action<UnityEngine.Object> onSelected)
|
||||
{
|
||||
_onSelected = onSelected;
|
||||
container.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
// Tab bar
|
||||
var tabBar = new VisualElement();
|
||||
tabBar.style.flexDirection = FlexDirection.Row;
|
||||
tabBar.style.borderBottomWidth = 1;
|
||||
tabBar.style.borderBottomColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.3f));
|
||||
container.Add(tabBar);
|
||||
|
||||
var btnSkill = BuildTabBtn("技能 (Skill)", 0, tabBar);
|
||||
var btnSeq = BuildTabBtn("序列 (Seq)", 1, tabBar);
|
||||
|
||||
var listArea = new VisualElement();
|
||||
listArea.style.flexGrow = 1;
|
||||
container.Add(listArea);
|
||||
|
||||
ShowTab(0, listArea, new[] { btnSkill, btnSeq });
|
||||
btnSkill.clicked += () => ShowTab(0, listArea, new[] { btnSkill, btnSeq });
|
||||
btnSeq.clicked += () => ShowTab(1, listArea, new[] { btnSkill, btnSeq });
|
||||
|
||||
_skillPane.Refresh();
|
||||
_seqPane.Refresh();
|
||||
}
|
||||
|
||||
public void BuildDetailPane(VisualElement container, UnityEngine.Object selected)
|
||||
{
|
||||
_header = new DetailHeader();
|
||||
_header.SetAsset(selected);
|
||||
_header.RenameRequested += name => OnRenameRequested(selected, name);
|
||||
container.Add(_header);
|
||||
|
||||
if (selected == null) return;
|
||||
|
||||
if (selected is BossSkillSO skill)
|
||||
{
|
||||
container.Add(BuildSkillCard(skill));
|
||||
container.Add(BuildActionBar(skill, SkillFolder, _skillPane));
|
||||
container.Add(SkillModule.MakeDivider());
|
||||
var insp = new InspectorElement(skill);
|
||||
insp.style.flexGrow = 1;
|
||||
container.Add(insp);
|
||||
}
|
||||
else if (selected is SkillSequenceSO seq)
|
||||
{
|
||||
container.Add(BuildSeqCard(seq));
|
||||
container.Add(BuildActionBar(seq, SeqFolder, _seqPane));
|
||||
container.Add(SkillModule.MakeDivider());
|
||||
var insp = new InspectorElement(seq);
|
||||
insp.style.flexGrow = 1;
|
||||
container.Add(insp);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnActivated()
|
||||
{
|
||||
_skillPane?.Refresh();
|
||||
_seqPane?.Refresh();
|
||||
}
|
||||
|
||||
// ── 内部 ─────────────────────────────────────────────────────────────
|
||||
|
||||
private Button BuildTabBtn(string text, int tabIdx, VisualElement bar)
|
||||
{
|
||||
var btn = new Button { text = text };
|
||||
btn.style.flexGrow = 1;
|
||||
btn.style.paddingTop = 5;
|
||||
btn.style.paddingBottom = 5;
|
||||
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.userData = tabIdx;
|
||||
bar.Add(btn);
|
||||
return btn;
|
||||
}
|
||||
|
||||
private void ShowTab(int tab, VisualElement area, Button[] tabBtns)
|
||||
{
|
||||
_activeTab = tab;
|
||||
area.Clear();
|
||||
|
||||
for (int i = 0; i < tabBtns.Length; i++)
|
||||
{
|
||||
if (i == tab)
|
||||
{
|
||||
tabBtns[i].style.borderBottomWidth = 2;
|
||||
tabBtns[i].style.borderBottomColor = new StyleColor(new Color(0.4f, 0.65f, 1f, 1f));
|
||||
tabBtns[i].style.opacity = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
tabBtns[i].style.borderBottomWidth = 0;
|
||||
tabBtns[i].style.opacity = 0.65f;
|
||||
}
|
||||
}
|
||||
|
||||
if (tab == 0) { _skillPane.style.flexGrow = 1; area.Add(_skillPane); }
|
||||
else { _seqPane.style.flexGrow = 1; area.Add(_seqPane); }
|
||||
}
|
||||
|
||||
private void OnRenameRequested(UnityEngine.Object asset, string newName)
|
||||
{
|
||||
var (ok, err) = AssetOperations.Rename(asset, newName);
|
||||
if (!ok) EditorUtility.DisplayDialog("重命名失败", err, "确定");
|
||||
else
|
||||
{
|
||||
_header.SetAsset(asset);
|
||||
if (_activeTab == 0) _skillPane.Invalidate();
|
||||
else _seqPane.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private static VisualElement BuildSkillCard(BossSkillSO s)
|
||||
{
|
||||
var card = SkillModule.MakeCard();
|
||||
SkillModule.AddChip(card, "分类", s.category.ToString());
|
||||
SkillModule.AddChip(card, "类型", s.skillType.ToString());
|
||||
SkillModule.AddChip(card, "模式数", (s.attackPatterns?.Length ?? 0).ToString());
|
||||
SkillModule.AddChip(card, "弱点窗口", (s.vulnerabilityWindows?.Length ?? 0).ToString());
|
||||
if (!string.IsNullOrEmpty(s.skillId))
|
||||
SkillModule.AddChip(card, "ID", s.skillId);
|
||||
return card;
|
||||
}
|
||||
|
||||
private static VisualElement BuildSeqCard(SkillSequenceSO s)
|
||||
{
|
||||
var card = SkillModule.MakeCard();
|
||||
SkillModule.AddChip(card, "步骤数", (s.steps?.Length ?? 0).ToString());
|
||||
SkillModule.AddChip(card, "循环", s.RepeatIfPlayerInRange ? "是" : "否");
|
||||
SkillModule.AddChip(card, "最大循环次数", s.MaxRepeatCount.ToString());
|
||||
return card;
|
||||
}
|
||||
|
||||
private VisualElement BuildActionBar<T>(T asset, string folder, SoListPane<T> pane)
|
||||
where T : ScriptableObject
|
||||
{
|
||||
var bar = SkillModule.MakeActionBar();
|
||||
new Button(() => { EditorGUIUtility.PingObject(asset); Selection.activeObject = asset; })
|
||||
{ text = "定位" }.AlsoAddTo(bar);
|
||||
new Button(() =>
|
||||
{
|
||||
var c = AssetOperations.Clone(asset, folder);
|
||||
if (c != null) pane.Refresh(c);
|
||||
}) { text = "克隆..." }.AlsoAddTo(bar);
|
||||
var del = new Button(() =>
|
||||
{
|
||||
if (AssetOperations.Delete(asset)) pane.Refresh(null);
|
||||
}) { text = "删除" };
|
||||
del.style.borderLeftColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f));
|
||||
del.style.borderRightColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f));
|
||||
del.style.borderTopColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f));
|
||||
del.style.borderBottomColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f));
|
||||
del.style.borderLeftWidth = 1;
|
||||
del.style.borderRightWidth = 1;
|
||||
del.style.borderTopWidth = 1;
|
||||
del.style.borderBottomWidth = 1;
|
||||
del.style.marginLeft = 8;
|
||||
del.AlsoAddTo(bar);
|
||||
return bar;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user