栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

Unity(十三):拓展编辑器 - Project面板、Hierarchy面板、Inspector面板

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity(十三):拓展编辑器 - Project面板、Hierarchy面板、Inspector面板

目录
    • ⚠️ Editor文件夹
    • 拓展Project面板
      • 拓展菜单
        • 效果
        • 注释
        • 源码
      • 拓展布局
        • 效果
        • 源码
      • 监听事件
    • 拓展Hierarchy面板
      • 拓展菜单
        • 效果
        • 源码
      • 修改编辑器默认的Image游戏对象
        • 效果
        • 源码
      • 拓展布局
        • 效果
        • 源码
    • 拓展Inspector面板
      • 拓展面板视图(布局)
        • 效果
        • 源码
      • 针对Unity内部已经重绘过的组件,拓展面板视图(布局)并保留Unity内部原有的绘制
        • `不保留Unity内部原有的绘制` 效果
        • `保留Unity内部原有的绘制` 效果
        • 源码
      • 禁用组件
        • 效果
        • 源码
      • 批量启用/禁用所选游戏对象的组件
        • 效果
        • 源码
      • 拓展菜单
        • 效果
        • 源码
      • 拓展菜单(获取当前游戏对象绑定的脚本)
        • 绑定的脚本
        • 效果
        • 源码

⚠️ Editor文件夹

Editor可以为任何文件夹的子文件夹
Editor下的文件为Unity编辑模式下的文件,非编辑模式下的脚本文件放在非Editor文件夹下即可

拓展Project面板 拓展菜单 效果



注释

需要在指定目录下拓展菜单可配置成如下示例:
[MenuItem("Assets/xx/xxxx", false, 1)](将在Assets菜单下创建xx/xxxx菜单)

源码
// 参数1: 菜单位置; 参数3: 菜单排序(数值越小越靠前);
[MenuItem("自定义工具箱/打印/打印当前游戏对象名称", false, 1)]
private static void Log1()
{
	if (Selection.activeObject != null)
        Debug.Log($"当前选择的游戏对象 ---> {Selection.activeObject.name}");
}

[MenuItem("自定义工具箱/打印/打印 Hello World", false, 0)]
private static void Log2()
{
    Debug.Log("Hello World");
}

[MenuItem("自定义工具箱/创建/立方体", false, 0)]
private static void CreateCube()
{
    // 创建立方体
    var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
    // 修改游戏对象名称
    cube.name = "立方体";
    // 添加刚体组件
    cube.AddComponent();
}

[MenuItem("自定义工具箱/创建/球体", false, 1)]
private static void CreateSphere()
{
    GameObject.CreatePrimitive(PrimitiveType.Sphere);
}
拓展布局 效果

源码
// InitializeOnLoadMethod 确保编译后首先调用此方法
[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod01()
{
    // 监听EditorApplication.projectWindowItemOnGUI委托,即可使用GUI来绘制UI元素
    EditorApplication.projectWindowItemOnGUI = delegate(string guid, Rect rect)
    {
        // 判断是否选中(向选中的元素添加UI元素)
        if (Selection.activeObject &&
            guid == AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject)))
        {
            const float width = 90f;
            rect.x += rect.width - width;
            rect.width = width;
            // 创建按钮
            var isClick = GUI.Button(rect, "打印当前对象");
            // 判断是否点击
            if (isClick)
                Debug.Log($"当前点击的游戏对象 ---> {Selection.activeObject.name}");
        }
    };
}
监听事件
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

// 监听事件
namespace Scripts_03.Editor
{
    public class WatchMethod : UnityEditor.AssetModificationProcessor
    {
        [InitializeOnLoadMethod]
        private static void InitializeOnLoadMethod()
        {
            // 全局监听Project视图下的资源是否发生变化(添加、删除、移动)
            EditorApplication.projectChanged += delegate { Debug.Log("Changed"); };
        }
        
        // 监听 资源点击、打开、修改 事件
        private static bool IsOpenForEdit(string[] paths, List outNotEditablePaths,
            StatusQueryOptions statusQueryOptions)
        {
            Debug.Log($"资源点击、打开、修改 ---> {string.Join(",", paths)}");
            // true允许在Unity中打开资源; false不允许在Unity中打开资源
            return false;
        }
        
        // 监听 资源即将被创建 事件
        private static void OnWillCreateAsset(string assetName)
        {
            Debug.Log($"资源即将被创建 ---> {assetName}");
        }
        
        // 监听 资源即将被保存 事件
        private static string[] OnWillSaveAssets(string[] paths)
        {
            Debug.Log($"资源即将被保存 ---> {string.Join(",", paths)}");
            return paths;
        }
        
        // 监听 资源即将被移动 事件
        private static AssetMoveResult OnWillMoveAsset(string sourcePath, string destinationPath)
        {
            Debug.Log($"资源即将被移动 ---> {sourcePath} ===> {destinationPath}");
            // DidNotMove可移动 DidMove不可移动 FailedMove错误移动
            return AssetMoveResult.DidNotMove;
        }
        
        // 监听 资源即将被删除 事件
        private static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options)
        {
            Debug.Log($"资源即将被删除 ---> {assetPath}");
            // DidNotDelete可删除 DidDelete不可删除 FailedDelete错误删除
            return AssetDeleteResult.DidNotDelete;
        }
    }
}
拓展Hierarchy面板 拓展菜单 效果

源码
[MenuItem("Window/Test/菜单 1")]
private static void TestMenu01()
{
    Debug.Log("TestMenu01");
}

[MenuItem("Window/Test/菜单 2")]
private static void TestMenu02()
{
    Debug.Log("TestMenu02");
}

[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod01()
{
    EditorApplication.hierarchyWindowItemOnGUI = delegate(int id, Rect rect)
    {
        // 修改菜单 右键显示
        // rect.Contains(Event.current.mousePosition)       点击在游戏对象上
        // Event.current.button == 1                        右键
        // Event.current.type               EventType.MouseDown || EventType.MouseUp
        if (Event.current is { button: 1 } && rect.Contains(Event.current.mousePosition) &&
            Event.current.type <= EventType.MouseUp)
        {
            // 当前点击的游戏对象
            GameObject selectedGameObject = EditorUtility.InstanceIDToObject(id) as GameObject;
            // 当游戏对象名称改为NewGameObject时,使用重写的菜单
            if (selectedGameObject!.name == "NewGameObject")
            {
                // 鼠标点击位置
                Vector2 mousePosition = Event.current.mousePosition;
                Rect position = new Rect(mousePosition.x, mousePosition.y, 0, 0);
                // 弹出菜单
                EditorUtility.DisplayPopupMenu(position, "Window/Test", null);
                // 不再执行原有的操作
                Event.current.Use();
            }
        }
    };
}
修改编辑器默认的Image游戏对象 效果


源码
[MenuItem("GameObject/UI/Image")]
private static void CreateCustomImage()
{
    if (Selection.activeTransform && Selection.activeTransform.GetComponentInParent())
    {
        // 创建Image游戏对象
        Image image = new GameObject("CustomImage").AddComponent();
        // 设置raycastTarget属性为false(原Image会默认设置为true)
        image.raycastTarget = false;
        // 修改颜色
        image.GetComponent().color = Color.cyan;
        // 设置父游戏对象
        image.transform.SetParent(Selection.activeTransform, false);
        // 选中
        Selection.activeTransform = image!.transform;
    }
}
拓展布局 效果

源码
[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod01()
{
    EditorApplication.hierarchyWindowItemOnGUI = delegate(int id, Rect rect)
    {
        // 拓展布局 选中后显示
        if (Selection.activeObject && id == Selection.activeObject.GetInstanceID())
        {
            const float width = 90f;
            rect.x += rect.width - width;
            rect.width = width;
            // 创建按钮
            var isClick = GUI.Button(rect, "打印当前对象");
            // 判断是否点击
            if (isClick)
                Debug.Log($"当前点击的游戏对象 ---> {Selection.activeObject.name}");
        }
    };
}
拓展Inspector面板 拓展面板视图(布局) 效果

源码
[CustomEditor(typeof(Camera))]
public class ExpandInspector1 : UnityEditor.Editor
{
    public override void OnInspectorGUI()
    {
        if (GUILayout.Button("拓展按钮"))
            Debug.Log("拓展按钮");
        base.OnInspectorGUI();
    }
}
针对Unity内部已经重绘过的组件,拓展面板视图(布局)并保留Unity内部原有的绘制

针对Unity内部已经重写了绘制方法的组件,内部的方法未公开,可以采用C#反射的方式调用内部未公开的方法来保留Unity对组件原有的绘制方式

不保留Unity内部原有的绘制 效果

保留Unity内部原有的绘制 效果

源码
[CustomEditor(typeof(Transform))]
public class ExpandInspector2 : UnityEditor.Editor
{
    // 针对Unity内部已经重写了绘制方法的组件,内部的方法未公开,可以采用C#反射的方式调用内部未公开的方法来保留Unity对组件原有的绘制方式

    private UnityEditor.Editor _editor;

    private void OnEnable()
    {
        // 通过反射获取到UnityEditor.TransformInspector对象
        _editor = CreateEditor(target,
            Assembly.GetAssembly(typeof(UnityEditor.Editor)).GetType("UnityEditor.TransformInspector", true));
    }

    public override void OnInspectorGUI()
    {
        if (GUILayout.Button("未被禁用的拓展按钮"))
            Debug.Log("未被禁用的拓展按钮");
        // 调用系统绘制方法
        _editor.OnInspectorGUI();
    }
}
禁用组件 效果

源码
[CustomEditor(typeof(Transform))]
public class ExpandInspector2 : UnityEditor.Editor
{
    // 针对Unity内部已经重写了绘制方法的组件,内部的方法未公开,可以采用C#反射的方式调用内部未公开的方法来保留Unity对组件原有的绘制方式

    private UnityEditor.Editor _editor;

    private void OnEnable()
    {
        // 通过反射获取到UnityEditor.TransformInspector对象
        _editor = CreateEditor(target,
            Assembly.GetAssembly(typeof(UnityEditor.Editor)).GetType("UnityEditor.TransformInspector", true));
    }

    public override void OnInspectorGUI()
    {
        if (GUILayout.Button("未被禁用的拓展按钮"))
            Debug.Log("未被禁用的拓展按钮");
        
        // 开始禁用(被包括范围内的控件将被禁用)
        GUI.enabled = false;
        // 调用系统绘制方法
        _editor.OnInspectorGUI();
        GUILayout.Button("被禁用的拓展按钮");
        // 结束禁用(被包括范围内的控件将被禁用)
        GUI.enabled = true;
    }
}
批量启用/禁用所选游戏对象的组件 效果

源码
// 启用禁用游戏对象组件
public static class ExpandInspector3
{
    [MenuItem("GameObject/3D Object/启用-禁用/禁用所选游戏对象的所有组件")]
    private static void Lock()
    {
        if (Selection.gameObjects != null)
        {
            foreach (GameObject go in Selection.gameObjects)
            {
                go.hideFlags = HideFlags.NotEditable;
            }
        }
    }

    [MenuItem("GameObject/3D Object/启用-禁用/启用所选游戏对象的所有组件")]
    private static void UnLock()
    {
        if (Selection.gameObjects != null)
        {
            foreach (GameObject go in Selection.gameObjects)
            {
                go.hideFlags = HideFlags.None;
            }
        }
    }
}
拓展菜单 效果


源码
// 配置Inspector的Context菜单
public static class ExpandInspector4
{
    [MenuItem("CONTEXT/Transform/菜单 01")]
    private static void Context01(MenuCommand menuCommand)
    {
        // 获取游戏对象名
        Debug.Log(menuCommand.context.name);
    }

    [MenuItem("CONTEXT/Light/菜单 02")]
    private static void Context02(MenuCommand menuCommand)
    {
        // 获取游戏对象名
        Debug.Log(menuCommand.context.name);
    }
}
拓展菜单(获取当前游戏对象绑定的脚本) 绑定的脚本

using UnityEngine;

namespace Scripts_03
{
    public class CapsuleScript : MonoBehaviour
    {
        public string msg = "Hello World!";
        private const string Msg = "Hello Lee!";

        public void SayHello()
        {
            Debug.Log($"{msg} --- {Msg} --- Hello");
        }
    }
}
效果


源码
internal class ExpandInspector5 : MonoBehaviour
{
#if UNITY_EDITOR
    // UNITY_EDITOR 只会在Editor模式下执行,发布后将被剔除掉

    // 通过菜单获取脚本内变量
    [MenuItem("CONTEXT/CapsuleScript/菜单 03")]
    private static void ScriptContext(MenuCommand menuCommand)
    {
        CapsuleScript capsuleScript = menuCommand.context as CapsuleScript;
        capsuleScript!.SayHello();
        // Debug可以查看脚本中的变量
        Debug.Log(capsuleScript);
    }

    // 覆盖原有菜单
    [MenuItem("CONTEXT/CapsuleScript/Remove Component")]
    private static void RemoveComponent(MenuCommand menuCommand)
    {
        Debug.Log("RemoveComponent");
        CapsuleScript capsuleScript = menuCommand.context as CapsuleScript;
        // 等一帧在删除自己
        EditorApplication.delayCall = delegate { DestroyImmediate(capsuleScript); };
    }
#endif
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/901610.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号