Odin Inspector 系列教程 --- 【小工具】ScriptableObjectCreator

本工具是基于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;//如不需要創建后自動關閉可將本行注釋
        }
    }
}

更多教程內容詳見:革命性Unity 編輯器擴展工具 --- Odin Inspector 系列教程

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。