美文网首页
Unity拓展编辑器(一)

Unity拓展编辑器(一)

作者: Dragon_boy | 来源:发表于2020-08-21 17:38 被阅读0次

    拓展Prooject视图

    放在Editor文件夹下,比如拓展右键菜单,一个示例脚本:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    public class Script01 : MonoBehaviour
    {
        [MenuItem("Assets/My Tools/ Tools 1", false, 2)]
        static void MyTools1()
        {
            Debug.Log(Selection.activeObject.name);
        }
    
        [MenuItem("Assets/My Tools/ Tools 2", false, 1)]
        static void MyTools2()
        {
            Debug.Log(Selection.activeObject.name);
        }
    }
    

    MenuItem方法的第一个参数是显示的菜单路径,第二个参数为false表明该方法在点击菜单选项时才会调用,反之第三个参数表示排序,数字越小越靠前:

    再比如,自定义创建物体的菜单,脚本:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    public class Script02 : MonoBehaviour
    {
        [MenuItem("Assets/Create/My Create/Cube", false, 2)]
        static void CreateCube()
        {
            GameObject.CreatePrimitive(PrimitiveType.Cube);
        }
    }
    

    上述脚本用于创建一个创建基本体的菜单。

    再比如,拓展布局,脚本:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    public class Script03 : MonoBehaviour
    {
        [InitializeOnLoadMethod]
        static void InitializeOnLoadMethod()
        {
            EditorApplication.projectWindowItemOnGUI = delegate (string guid, Rect selectionRect)
            {
                if (Selection.activeObject && guid == AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject)))
                {
                    float width = 50.0f;
                    selectionRect.x += (selectionRect.width - width);
                    selectionRect.y += 2f;
                    selectionRect.width = width;
                    GUI.color = Color.red;
    
                    if (GUI.Button(selectionRect, "click"))
                    {
                        Debug.LogFormat("click:{0}", Selection.activeObject.name);
                    }
                    GUI.color = Color.white;
                }
            };
        }
    }
    

    该脚本会在Project元素旁设置一个按钮,打印一些信息,InitializeOnLoadMethod可以让该函数成为自动初始化的函数,无序用户手动操作:

    Project视图中,资源要进行比较好的规划,我们可以用监听事件来实现,可以根据文件原始位置以及要移动过去的位置来判断移动是否合法。脚本类需继承UnityEditor.AssetModificationProcessor:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using UnityEditor.VersionControl;
    
    public class Script04 : UnityEditor.AssetModificationProcessor
    {
        [InitializeOnLoadMethod]
        static void InitializeOnLoadMethod()
        {
            //全局监听Project视图下的资源是否发生变化
            EditorApplication.projectWindowChanged = delegate () {
                Debug.Log("change");
            };
        }
    
        // 监听双击鼠标左键,打开资源的事件
        public static bool IsOpenForEdit(string assetPath, out string message)
        {
            message = null;
            Debug.LogFormat("assetPath:{0}", assetPath);
            return true;
        }
    
        //监听资源创建事件
        public static void OnWillCreateAsset(string path)
        {
            Debug.LogFormat("path:{0}", path);
        }
    
        //监听资源被保存事件
        public static string[] OnWillSaveAssets(string[] paths)
        {
            if(paths!=null)
            {
                Debug.LogFormat("path:{0}", string.Join(",", paths));
            }
            return paths;
        }
    
        //监听资源被移动事件
        public static AssetMoveResult OnWillMoveAsset(string oldPath, string newPath)
        {
            Debug.LogFormat("from:{0} to: {1}", oldPath, newPath);
    
            return AssetMoveResult.DidMove;
        }
    
        // 监听资源被删除事件
        public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions option)
        {
            Debug.LogFormat("delete:{0}", assetPath);
            return AssetDeleteResult.DidDelete;
        }
    }
    

    拓展Hierarchy视图

    在Hierarchy视图中右键可以弹出一些创建物体的按钮,可以自定义一些按钮,如:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    public class Script05 : MonoBehaviour
    {
        [MenuItem("GameObject/My Create/Cube", false, 0)]
        static void CreateCube()
        {
            GameObject.CreatePrimitive(PrimitiveType.Cube);
        }
    }
    

    脚本写法和之前的没区别,关键在于MenuItem的路径。

    同样,我们也可以对布局进行拓展,比如:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    public class Script06 : MonoBehaviour
    {
        [InitializeOnLoadMethod]
        static void InitializeOnLoadMethod()
        {
            EditorApplication.hierarchyWindowItemOnGUI = delegate (int instanceID, Rect selectionRect)
            {
                if(Selection.activeObject && instanceID == Selection.activeObject.GetInstanceID())
                {
                    float width = 50f;
                    float height = 20f;
                    selectionRect.x += (selectionRect.width - width);
                    selectionRect.width = width;
                    selectionRect.height = height;
    
                    // 点击事件
                    if(GUI.Button(selectionRect, AssetDatabase.LoadAssetAtPath<Texture>("Assets/unity.png")))
                    {
                        Debug.LogFormat("click:{0}", Selection.activeObject.name);
                    }
                }
            };
        }
    }
    

    该脚本会在Hierarchy视图的元素旁生成一个按钮,点击会打印一些信息。

    除了修改右键菜单,我们还可以重写菜单,脚本示例:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    public class Script07 : MonoBehaviour
    {
        [MenuItem("Window/Test/dragon")]
        static void Test()
        {
    
        }
    
        [MenuItem("Window/Test/dragon_boy")]
        static void Test1()
        {
    
        }
    
        [MenuItem("Window/Test/DragonBaby/dragon_baby")]
        static void Test2()
        {
    
        }
    
        [InitializeOnLoadMethod]
        static void StartInitializeOnLoadMethod()
        {
            EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyGUI;
        }
    
        static void OnHierarchyGUI(int instanceID, Rect selectionRect)
        {
            if(Event.current!=null&&selectionRect.Contains(Event.current.mousePosition)&&Event.current.button ==1&&Event.current.type<=EventType.MouseUp)
            {
                GameObject selectedGameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
    
                if(selectedGameObject)
                {
                    Vector2 mousePosition = Event.current.mousePosition;
    
                    EditorUtility.DisplayPopupMenu(new Rect(mousePosition.x, mousePosition.y, 0, 0), "Window/Test", null);
                    Event.current.Use();
                }
            }
        }
    }
    

    这样,选中一个我们创建的物体,它的右键菜单就完全被我们创建的三个菜单项替代了。Event.current可以获得当前事件,EditorUtility.DisplayPopupMenu可以弹出自定义菜单,Event.current.Use()可以不执行原有的菜单操作。

    我们还可以重写系统自带菜单的行为,比如UI中的image组件,可以取消它的RaycastTarget默认勾选:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using UnityEngine.UI;
    
    public class Script08 : MonoBehaviour
    {
        [MenuItem("GameObject/UI/Image")]
        static void CreateImage()
        {
            if(Selection.activeTransform)
            {
                if(Selection.activeTransform.GetComponentInParent<Canvas>())
                {
                    Image image = new GameObject("image").AddComponent<Image>();
                    image.raycastTarget = false;
                    image.transform.SetParent(Selection.activeTransform, false);
    
                    Selection.activeTransform = image.transform;
                }
            }
        }
    }
    

    拓展Inspector视图

    对于源生组件,可以添加按钮,只不过只能在最上面或最下面拓展,如摄像机:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    [CustomEditor(typeof(Camera))]
    public class Script09 : Editor
    {
        public override void OnInspectorGUI()
        {
            if (GUILayout.Button("拓展")) { }
            base.OnInspectorGUI();
        }
    }
    

    不过对于继承组件,Unity内部可能已经重写了绘制方法,外部访问不了内部代码。Unity将Editor绘制方法封装在内部的DLL文件中,我们可以利用C#的反射特性来调用其中的方法:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using System.Reflection;
    
    [CustomEditor(typeof(Transform))]
    public class Script10 : Editor
    {
        private Editor m_Editor;
        private void OnEnable()
        {
            m_Editor = Editor.CreateEditor(target, Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.TransformInspector", true));
        }
    
        public override void OnInspectorGUI()
        {
            if (GUILayout.Button("拓展")) { }
            m_Editor.OnInspectorGUI();
        }
    }
    

    OnEnable中通过反射得到UnityEditor.TransformInspector对象,然后可以调用内部的OnInspectorGUI方法。


    还可以设置组件状态,让其不可编辑,比如Transform组件,但不影响我们自定义的拓展按钮:
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using System.Reflection;
    
    [CustomEditor(typeof(Transform))]
    public class Script11 : Editor
    {
        private Editor m_Editor;
        private void OnEnable()
        {
            m_Editor = Editor.CreateEditor(target, Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.TransformInspector", true));
        }
    
        public override void OnInspectorGUI()
        {
            if (GUILayout.Button("拓展上")) { }
            GUI.enabled = false;
            m_Editor.OnInspectorGUI();
            GUI.enabled = true;
            if (GUILayout.Button("拓展下")) { }
        }
    }
    

    拓展Scene视图

    比如在视图中绘制辅助元素,比如Gizmo:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Script12 : MonoBehaviour
    {
        private void OnDrawGizmosSelected()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawLine(transform.position, Vector3.one);
            Gizmos.DrawCube(Vector3.one, Vector3.one);
        }
    }
    

    上面的脚本会在选中某一物体时,添加一条红色辅助线,并在末端绘制一个立方体。

    还可以绘制辅助UI,添加EditorGUI,代码在Handles.BeginGUI()Handles.EndGUI

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    
    [CustomEditor(typeof(Camera))]
    public class Script16 : Editor
    {
        private void OnSceneGUI()
        {
            Camera camera = target as Camera;
            if(camera!=null)
            {
                Handles.color = Color.red;
                Handles.Label(camera.transform.position, camera.transform.position.ToString());
    
                Handles.BeginGUI();
                GUI.backgroundColor = Color.red;
                if(GUILayout.Button("click", GUILayout.Width(200f)))
                {
                    Debug.LogFormat("Label");
                    Handles.EndGUI();
                }
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Unity拓展编辑器(一)

          本文链接:https://www.haomeiwen.com/subject/xzxyjktx.html