Unity拓展编辑器(一)

2020-08-21  本文已影响0人  Dragon_boy

拓展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();
            }
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读