本工具是基于Odin,為創建ScriptableObject對應的asset文件制作的菜單,即開即用
示例代碼已經做好注釋,易于理解、魔改
轉載自:https://bitbucket.org/snippets/Bjarkeck/keRbr4
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class ScriptableObjectCreator : OdinMenuEditorWindow
{
/// <summary>
/// 獲取繼承 ScriptableObject 且不是Editor相關的所有自定義類(也就是自己編寫的類)
/// </summary>
static HashSet<Type> scriptableObjectTypes = AssemblyUtilities.GetTypes(AssemblyTypeFlags.CustomTypes)
.Where(t =>
t.IsClass &&
typeof(ScriptableObject).IsAssignableFrom(t) &&
!typeof(EditorWindow).IsAssignableFrom(t) &&
!typeof(Editor).IsAssignableFrom(t))
.ToHashSet();
[MenuItem("Assets/Create Scriptable Object", priority = -1000)]
private static void ShowDialog()
{
var path = "Assets";
var obj = Selection.activeObject; //當前鼠標選中的 Object
if (obj && AssetDatabase.Contains(obj))
{
path = AssetDatabase.GetAssetPath(obj);
if (!Directory.Exists(path))//主要用來判斷所選的是文件還是文件夾
{
path = Path.GetDirectoryName(path);//如果是文件則獲取對應文件夾的全名稱
}
}
//設置窗口對應屬性
var window = CreateInstance<ScriptableObjectCreator>();
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(800, 500);//設置窗口的寬和高
window.titleContent = new GUIContent(path);
window.targetFolder = path.Trim('/');//避免出現 / 造成路徑不對
window.ShowUtility();
}
/// <summary>
/// 選中的 ScriptableObject(等待創建)
/// </summary>
private ScriptableObject previewObject;
/// <summary>
/// 創建 ScriptableObject 時文件存儲的目標文件夾
/// </summary>
private string targetFolder;
private Vector2 scroll;
private Type SelectedType
{
get
{
var m = MenuTree.Selection.LastOrDefault();//因為可以多選,所以返回選中的是一個列表,這里返回的是列表的最后一個Object
return m == null ? null : m.Value as Type;
}
}
protected override OdinMenuTree BuildMenuTree()
{
MenuWidth = 300;//菜單的寬度
WindowPadding = Vector4.zero;
OdinMenuTree tree = new OdinMenuTree(false);//不支持多選
tree.Config.DrawSearchToolbar = true;//開啟搜索狀態
tree.DefaultMenuStyle = OdinMenuStyle.TreeViewStyle;//菜單設置成樹形模式
//篩選所有非抽象的類 并獲取對應的路徑
tree.AddRange(scriptableObjectTypes.Where(x => !x.IsAbstract), GetMenuPathForType).AddThumbnailIcons();
tree.SortMenuItemsByName();
tree.Selection.SelectionConfirmed += x =>
{
Debug.Log($"雙擊確認并創建:{x}");
this.CreateAsset();
};
tree.Selection.SelectionChanged += e =>
{
//每當選擇發生更改時發生進行回調2次,一次SelectionCleared 一次是ItemAdded
if (this.previewObject && !AssetDatabase.Contains(this.previewObject))
{
DestroyImmediate(previewObject);
}
if (e != SelectionChangedType.ItemAdded)
{
return;
}
var t = SelectedType;
if (t != null && !t.IsAbstract)
{
previewObject = CreateInstance(t) as ScriptableObject;
}
};
return tree;
}
private string GetMenuPathForType(Type t)
{
if (t != null && scriptableObjectTypes.Contains(t))
{
var name = t.Name.Split('`').First().SplitPascalCase();//主要是為了去除泛型相關 例如:Sirenix.Utilities.GlobalConfig`1[Sirenix.Serialization.GlobalSerializationConfig]
return GetMenuPathForType(t.BaseType) + "/" + name;
}
return "";
}
protected override IEnumerable<object> GetTargets()
{
yield return previewObject;
}
protected override void DrawEditor(int index)
{
//scroll 內容滑動條的XY坐標
scroll = GUILayout.BeginScrollView(scroll);
{
base.DrawEditor(index);
}
GUILayout.EndScrollView();
if (this.previewObject)
{
GUILayout.FlexibleSpace();//插入一個空隙
SirenixEditorGUI.HorizontalLineSeparator(5);//插入一個水平分割線
if (GUILayout.Button("Create Asset", GUILayoutOptions.Height(30)))
{
CreateAsset();
}
}
}
private void CreateAsset()
{
if (previewObject)
{
var dest = targetFolder + "/new " + MenuTree.Selection.First().Name.ToLower() + ".asset";
dest = AssetDatabase.GenerateUniqueAssetPath(dest);//創建唯一路徑 重名后綴 +1
Debug.Log($"要創建的為{previewObject}");
AssetDatabase.CreateAsset(previewObject, dest);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Selection.activeObject = previewObject;
EditorApplication.delayCall += Close;//如不需要創建后自動關閉可將本行注釋
}
}
}