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:
@@ -35,6 +35,7 @@ namespace BaseGames.Editor
|
||||
public string TypeName;
|
||||
public string AssetPath;
|
||||
public ScriptableObject Asset;
|
||||
public string Guid;
|
||||
}
|
||||
|
||||
private readonly List<CategoryEntry> _categories = new();
|
||||
@@ -50,6 +51,7 @@ namespace BaseGames.Editor
|
||||
private ListView _assetList;
|
||||
private TextField _searchField;
|
||||
private Label _statusLabel;
|
||||
private VisualElement _renameContainer;
|
||||
|
||||
// ── 菜单入口 ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -141,12 +143,16 @@ namespace BaseGames.Editor
|
||||
var rightPane = new VisualElement();
|
||||
rightPane.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
// 列标题行
|
||||
// 列标题行:paddingRight 额外加 13px(滚动条宽度)确保列对齐
|
||||
var colHeader = new VisualElement();
|
||||
colHeader.AddToClassList("pane-header");
|
||||
colHeader.style.flexDirection = FlexDirection.Row;
|
||||
colHeader.style.flexDirection = FlexDirection.Row;
|
||||
colHeader.style.paddingLeft = 8;
|
||||
colHeader.style.paddingRight = 21; // 8 + 13 (滚动条占位)
|
||||
colHeader.style.paddingTop = 4;
|
||||
colHeader.style.paddingBottom = 4;
|
||||
colHeader.Add(MakeHeaderLabel("资产名", true, 0));
|
||||
colHeader.Add(MakeHeaderLabel("类型", false, 170));
|
||||
colHeader.Add(MakeHeaderLabel("类型", false, 190));
|
||||
colHeader.Add(MakeHeaderLabel("路径", true, 0));
|
||||
rightPane.Add(colHeader);
|
||||
|
||||
@@ -160,19 +166,30 @@ namespace BaseGames.Editor
|
||||
_assetList.style.flexGrow = 1;
|
||||
_assetList.selectionChanged += _ => OnAssetPicked();
|
||||
_assetList.itemsChosen += _ => FocusProjectWindow();
|
||||
_assetList.AddManipulator(new ContextualMenuManipulator(BuildAssetContextMenu));
|
||||
rightPane.Add(_assetList);
|
||||
|
||||
// 滚动条常显,确保 header 列与内容列对齐
|
||||
_assetList.schedule.Execute(() =>
|
||||
{
|
||||
var sv = _assetList.Q<ScrollView>();
|
||||
if (sv != null) sv.verticalScrollerVisibility = ScrollerVisibility.AlwaysVisible;
|
||||
});
|
||||
|
||||
// 状态栏
|
||||
_statusLabel = new Label("—");
|
||||
_statusLabel.style.paddingLeft = 8;
|
||||
_statusLabel.style.paddingTop = 3;
|
||||
_statusLabel.style.paddingBottom = 3;
|
||||
_statusLabel.style.borderTopWidth = 1;
|
||||
_statusLabel.style.borderTopColor = new Color(0.15f, 0.15f, 0.15f);
|
||||
_statusLabel.style.color = new Color(0.58f, 0.58f, 0.58f);
|
||||
_statusLabel.style.fontSize = 11;
|
||||
_statusLabel.style.borderTopColor = new Color(0.0f, 0.0f, 0.0f, 0.20f);
|
||||
_statusLabel.AddToClassList("so-path-label");
|
||||
rightPane.Add(_statusLabel);
|
||||
|
||||
// 重命名容器(选中资产后显示)
|
||||
_renameContainer = new VisualElement { style = { display = DisplayStyle.None } };
|
||||
rightPane.Add(_renameContainer);
|
||||
|
||||
split.Add(leftPane);
|
||||
split.Add(rightPane);
|
||||
rootVisualElement.Add(split);
|
||||
@@ -182,7 +199,9 @@ namespace BaseGames.Editor
|
||||
{
|
||||
var lbl = new Label(text);
|
||||
lbl.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
lbl.style.overflow = Overflow.Hidden;
|
||||
lbl.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||
lbl.style.overflow = Overflow.Hidden;
|
||||
lbl.style.minWidth = 0;
|
||||
if (grow) lbl.style.flexGrow = 1;
|
||||
if (fixedWidth > 0) lbl.style.width = fixedWidth;
|
||||
return lbl;
|
||||
@@ -219,22 +238,22 @@ namespace BaseGames.Editor
|
||||
|
||||
var nameEl = new Label { name = "n" };
|
||||
nameEl.style.flexGrow = 1;
|
||||
nameEl.style.minWidth = 0;
|
||||
nameEl.style.overflow = Overflow.Hidden;
|
||||
nameEl.style.textOverflow = TextOverflow.Ellipsis;
|
||||
|
||||
var typeEl = new Label { name = "t" };
|
||||
typeEl.style.width = 170;
|
||||
typeEl.style.overflow = Overflow.Hidden;
|
||||
typeEl.style.width = 190;
|
||||
typeEl.style.minWidth = 0;
|
||||
typeEl.style.overflow = Overflow.Hidden;
|
||||
typeEl.style.textOverflow = TextOverflow.Ellipsis;
|
||||
typeEl.style.color = new Color(0.52f, 0.80f, 1.00f);
|
||||
typeEl.style.fontSize = 11;
|
||||
typeEl.AddToClassList("so-type-label");
|
||||
|
||||
var pathEl = new Label { name = "p" };
|
||||
pathEl.style.flexGrow = 1;
|
||||
pathEl.style.overflow = Overflow.Hidden;
|
||||
pathEl.style.flexGrow = 1;
|
||||
pathEl.style.overflow = Overflow.Hidden;
|
||||
pathEl.style.textOverflow = TextOverflow.Ellipsis;
|
||||
pathEl.style.color = new Color(0.48f, 0.48f, 0.48f);
|
||||
pathEl.style.fontSize = 10;
|
||||
pathEl.AddToClassList("so-path-label");
|
||||
|
||||
row.Add(nameEl);
|
||||
row.Add(typeEl);
|
||||
@@ -262,11 +281,38 @@ namespace BaseGames.Editor
|
||||
private void OnAssetPicked()
|
||||
{
|
||||
int idx = _assetList.selectedIndex;
|
||||
if (idx < 0 || idx >= _filtered.Count) return;
|
||||
if (idx < 0 || idx >= _filtered.Count)
|
||||
{
|
||||
_renameContainer.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
var asset = _filtered[idx].Asset;
|
||||
if (asset == null) return;
|
||||
if (asset == null)
|
||||
{
|
||||
_renameContainer.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
EditorGUIUtility.PingObject(asset);
|
||||
Selection.activeObject = asset;
|
||||
|
||||
// 更新重命名栏
|
||||
_renameContainer.Clear();
|
||||
var (pfx, conv) = EditorScaffoldUtils.GetAssetPrefixInfo(asset.GetType());
|
||||
var entry = _filtered[idx];
|
||||
string savedGuid = entry.Guid;
|
||||
_renameContainer.Add(EditorScaffoldUtils.MakeRenameBar(asset, pfx, conv, _ =>
|
||||
{
|
||||
Refresh();
|
||||
int ni = _filtered.FindIndex(e => e.Guid == savedGuid);
|
||||
if (ni >= 0) _assetList.SetSelection(ni);
|
||||
}));
|
||||
|
||||
// 删除按钮
|
||||
var btnDelete = new Button(() => DeleteCurrentAsset(asset)) { text = "删除…" };
|
||||
btnDelete.AddToClassList("action-button--danger");
|
||||
_renameContainer.Add(btnDelete);
|
||||
|
||||
_renameContainer.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
|
||||
private static void FocusProjectWindow()
|
||||
@@ -293,6 +339,7 @@ namespace BaseGames.Editor
|
||||
TypeName = asset.GetType().Name,
|
||||
AssetPath = path,
|
||||
Asset = asset,
|
||||
Guid = guid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -331,6 +378,26 @@ namespace BaseGames.Editor
|
||||
ApplyFilter();
|
||||
}
|
||||
|
||||
private void DeleteCurrentAsset(ScriptableObject asset)
|
||||
{
|
||||
if (!EditorScaffoldUtils.DeleteSOAsset(asset)) return;
|
||||
_renameContainer.Clear();
|
||||
_renameContainer.style.display = DisplayStyle.None;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void BuildAssetContextMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
int idx = _assetList.selectedIndex;
|
||||
if (idx < 0 || idx >= _filtered.Count) return;
|
||||
var asset = _filtered[idx].Asset;
|
||||
if (asset == null) return;
|
||||
evt.menu.AppendAction("在 Project 中定位", _ => EditorScaffoldUtils.PingAndSelect(asset));
|
||||
evt.menu.AppendAction("在 Inspector 中打开", _ => Selection.activeObject = asset);
|
||||
evt.menu.AppendSeparator();
|
||||
evt.menu.AppendAction("删除…", _ => DeleteCurrentAsset(asset));
|
||||
}
|
||||
|
||||
private void ApplyFilter()
|
||||
{
|
||||
_filtered.Clear();
|
||||
|
||||
Reference in New Issue
Block a user