Files
aibis-dream/Assets/Plugins/SOEditorTool/Editor/UniversalSOEditor.cs
T
2025-12-09 19:21:59 +08:00

428 lines
14 KiB
C#

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System;
using System.Linq;
public class UniversalSOManager : EditorWindow
{
// === 布局常量 ===
private const float SIDEBAR_WIDTH = 160f;
private const float ASSET_LIST_WIDTH = 220f;
// === 核心数据 ===
private SOEditorConfig _config;
private Type _selectedType; // 现在直接存 Type,不再存 MonoScript
// === 列表与编辑 ===
private List<ScriptableObject> _assetList = new List<ScriptableObject>();
private ScriptableObject _selectedAsset;
private Editor _cachedEditor;
// === 状态 ===
private string _searchRootPath = "Assets/SOGameData";
private Vector2 _scrollPosTypes;
private Vector2 _scrollPosAssets;
private Vector2 _scrollPosInspector;
private string _searchString = "";
private string _newAssetName = "";
[MenuItem("Tools/万能SO编辑器 (Universal SO Manager)")]
public static void ShowWindow()
{
GetWindow<UniversalSOManager>("SO Manager");
}
private void OnEnable()
{
string configPath = EditorPrefs.GetString("UniversalSOManager_ConfigPath", "");
if (!string.IsNullOrEmpty(configPath))
{
_config = AssetDatabase.LoadAssetAtPath<SOEditorConfig>(configPath);
}
// 加载保存的资源路径
_searchRootPath = EditorPrefs.GetString("UniversalSOManager_SearchRootPath", "Assets/SOGameData");
}
private void OnDisable()
{
if (_cachedEditor != null) DestroyImmediate(_cachedEditor);
if (_config != null)
{
EditorPrefs.SetString("UniversalSOManager_ConfigPath", AssetDatabase.GetAssetPath(_config));
}
// 保存资源路径
EditorPrefs.SetString("UniversalSOManager_SearchRootPath", _searchRootPath);
}
private void OnGUI()
{
DrawTopGlobalBar();
if (_config == null)
{
DrawEmptyState();
return;
}
EditorGUILayout.BeginHorizontal();
{
DrawSidebar();
EditorGUILayout.BeginVertical();
{
DrawTypeOperationHeader();
EditorGUILayout.BeginHorizontal();
{
DrawAssetList();
DrawInspector();
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndHorizontal();
}
// ------------------------------------------------------------------------
// UI 绘制函数
// ------------------------------------------------------------------------
private void DrawTopGlobalBar()
{
EditorGUILayout.BeginHorizontal("box", GUILayout.Height(30));
{
GUILayout.Label("资源路径:", GUILayout.Width(60));
_searchRootPath = EditorGUILayout.TextField(_searchRootPath, GUILayout.Width(150));
if (GUILayout.Button("...", EditorStyles.miniButton, GUILayout.Width(25)))
{
string path = EditorUtility.OpenFolderPanel("选择根目录", Application.dataPath, "");
if (!string.IsNullOrEmpty(path) && path.StartsWith(Application.dataPath))
{
_searchRootPath = "Assets" + path.Substring(Application.dataPath.Length);
RefreshAssetList();
}
}
GUILayout.Space(20);
GUILayout.Label("Config文件:", GUILayout.Width(70));
EditorGUI.BeginChangeCheck();
_config = (SOEditorConfig)EditorGUILayout.ObjectField(_config, typeof(SOEditorConfig), false, GUILayout.Width(200));
if (EditorGUI.EndChangeCheck())
{
_selectedType = null;
_assetList.Clear();
_selectedAsset = null;
GUIUtility.ExitGUI();
}
// 快捷编辑 Config 按钮
if (_config != null)
{
if (GUILayout.Button("编辑Config", EditorStyles.miniButton, GUILayout.Width(80)))
{
Selection.activeObject = _config;
}
}
GUILayout.FlexibleSpace();
if (GUILayout.Button("强制刷新", EditorStyles.toolbarButton, GUILayout.Width(80)))
{
AssetDatabase.Refresh();
RefreshAssetList();
}
}
EditorGUILayout.EndHorizontal();
}
private void DrawSidebar()
{
EditorGUILayout.BeginVertical("box", GUILayout.Width(SIDEBAR_WIDTH), GUILayout.ExpandHeight(true));
GUILayout.Label("SO 种类", EditorStyles.boldLabel);
GUILayout.Space(5);
_scrollPosTypes = EditorGUILayout.BeginScrollView(_scrollPosTypes);
if (_config != null && _config.targetTypeNames != null)
{
foreach (var typeName in _config.targetTypeNames)
{
// 解析类型
Type type = GetTypeByName(typeName);
if (type == null)
{
// 类型丢失(可能代码改名了)
GUI.color = Color.red;
GUILayout.Label($"丢失: {typeName}");
GUI.color = Color.white;
continue;
}
bool isSelected = (_selectedType == type);
if (isSelected) GUI.backgroundColor = new Color(0.6f, 0.8f, 1f);
// 使用短名显示,但在 Tooltip 显示全名
if (GUILayout.Button(new GUIContent(type.Name, type.FullName), EditorStyles.miniButton, GUILayout.Height(28)))
{
SelectType(type);
}
GUI.backgroundColor = Color.white;
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
private void DrawTypeOperationHeader()
{
EditorGUILayout.BeginVertical("box", GUILayout.Height(60), GUILayout.ExpandWidth(true));
string title = _selectedType != null ? _selectedType.Name : "请选择种类";
GUILayout.Label($"当前操作: {title}", EditorStyles.boldLabel);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
{
GUILayout.Label("搜索:", GUILayout.Width(40));
string newSearch = EditorGUILayout.TextField(_searchString, EditorStyles.toolbarSearchField, GUILayout.Width(200));
if (newSearch != _searchString)
{
_searchString = newSearch;
RefreshAssetList();
}
GUILayout.FlexibleSpace();
GUILayout.Label("新名称:", GUILayout.Width(50));
_newAssetName = EditorGUILayout.TextField(_newAssetName, GUILayout.Width(150));
EditorGUI.BeginDisabledGroup(_selectedType == null);
if (GUILayout.Button("新增 (Create)", EditorStyles.miniButtonRight, GUILayout.Width(100)))
{
CreateAsset();
}
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void DrawAssetList()
{
EditorGUILayout.BeginVertical("box", GUILayout.Width(ASSET_LIST_WIDTH), GUILayout.ExpandHeight(true));
GUILayout.Label("资源列表", EditorStyles.miniLabel);
_scrollPosAssets = EditorGUILayout.BeginScrollView(_scrollPosAssets);
if (_assetList != null)
{
for (int i = 0; i < _assetList.Count; i++)
{
var asset = _assetList[i];
if (asset == null) continue;
EditorGUILayout.BeginHorizontal("box");
{
if (_selectedAsset == asset) GUI.backgroundColor = Color.green;
if (GUILayout.Button(asset.name, EditorStyles.label, GUILayout.Height(22)))
{
SelectAsset(asset);
}
GUI.backgroundColor = Color.white;
if (GUILayout.Button(EditorGUIUtility.IconContent("d_ViewToolOrbit"), EditorStyles.iconButton, GUILayout.Width(22)))
EditorGUIUtility.PingObject(asset);
if (GUILayout.Button(EditorGUIUtility.IconContent("d_TreeEditor.Duplicate"), EditorStyles.iconButton, GUILayout.Width(22)))
DuplicateAsset(asset);
if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Trash"), EditorStyles.iconButton, GUILayout.Width(22)))
{
if (EditorUtility.DisplayDialog("删除确认", $"确定删除 {asset.name} 吗?", "删除", "取消"))
{
DeleteAsset(asset);
GUIUtility.ExitGUI();
}
}
}
EditorGUILayout.EndHorizontal();
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
private void DrawInspector()
{
EditorGUILayout.BeginVertical("box", GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
if (_selectedAsset != null)
{
GUILayout.Label("属性编辑", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("文件名:", GUILayout.Width(50));
string newName = EditorGUILayout.DelayedTextField(_selectedAsset.name);
if (newName != _selectedAsset.name) RenameAsset(_selectedAsset, newName);
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
_scrollPosInspector = EditorGUILayout.BeginScrollView(_scrollPosInspector);
if (_cachedEditor == null || _cachedEditor.target != _selectedAsset)
{
if (_cachedEditor != null) DestroyImmediate(_cachedEditor);
_cachedEditor = Editor.CreateEditor(_selectedAsset);
}
if (_cachedEditor != null)
{
EditorGUI.BeginChangeCheck();
_cachedEditor.OnInspectorGUI();
if (EditorGUI.EndChangeCheck()) EditorUtility.SetDirty(_selectedAsset);
}
EditorGUILayout.EndScrollView();
}
else
{
GUILayout.FlexibleSpace();
GUILayout.Label("<< 请选择资源", EditorStyles.centeredGreyMiniLabel);
GUILayout.FlexibleSpace();
}
EditorGUILayout.EndVertical();
}
private void DrawEmptyState()
{
EditorGUILayout.BeginVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.HelpBox("请在顶部指定一个 SOEditorConfig 配置文件。", MessageType.Info);
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
// ------------------------------------------------------------------------
// 逻辑处理
// ------------------------------------------------------------------------
// 辅助:从字符串获取类型 (遍历所有程序集)
private Type GetTypeByName(string fullName)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var type = assembly.GetType(fullName);
if (type != null) return type;
}
return null;
}
private void SelectType(Type type)
{
_selectedType = type;
_selectedAsset = null;
_newAssetName = "New " + type.Name;
RefreshAssetList();
}
private void SelectAsset(ScriptableObject asset)
{
_selectedAsset = asset;
GUI.FocusControl(null);
}
private void RefreshAssetList()
{
if (_selectedType == null) return;
_assetList.Clear();
// 查找资源
string[] guids = AssetDatabase.FindAssets($"t:{_selectedType.Name}", new[] { _searchRootPath });
foreach (var guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
// 严格类型检查 (支持继承)
if (asset != null && _selectedType.IsAssignableFrom(asset.GetType()))
{
if (string.IsNullOrEmpty(_searchString) || asset.name.ToLower().Contains(_searchString.ToLower()))
{
_assetList.Add(asset);
}
}
}
_assetList.Sort((a, b) => string.Compare(a.name, b.name, StringComparison.Ordinal));
}
private void CreateAsset()
{
if (_selectedType == null) return;
string targetFolder = $"{_searchRootPath}/{_selectedType.Name}";
if (!AssetDatabase.IsValidFolder(targetFolder))
{
if (AssetDatabase.IsValidFolder(_searchRootPath))
AssetDatabase.CreateFolder(_searchRootPath, _selectedType.Name);
else
targetFolder = _searchRootPath;
}
string fullPath = $"{targetFolder}/{_newAssetName}.asset";
fullPath = AssetDatabase.GenerateUniqueAssetPath(fullPath);
var instance = ScriptableObject.CreateInstance(_selectedType);
AssetDatabase.CreateAsset(instance, fullPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
RefreshAssetList();
SelectAsset(instance);
}
private void DuplicateAsset(ScriptableObject asset)
{
string path = AssetDatabase.GetAssetPath(asset);
string newPath = AssetDatabase.GenerateUniqueAssetPath(path);
AssetDatabase.CopyAsset(path, newPath);
AssetDatabase.SaveAssets();
RefreshAssetList();
}
private void DeleteAsset(ScriptableObject asset)
{
string path = AssetDatabase.GetAssetPath(asset);
AssetDatabase.DeleteAsset(path);
AssetDatabase.SaveAssets();
if (_selectedAsset == asset) _selectedAsset = null;
RefreshAssetList();
}
private void RenameAsset(ScriptableObject asset, string newName)
{
string path = AssetDatabase.GetAssetPath(asset);
AssetDatabase.RenameAsset(path, newName);
AssetDatabase.SaveAssets();
RefreshAssetList();
}
}