Unity编辑器扩展之Hierarchy面板扩展

内容将会持续更新,有错误的地方欢迎指正,谢谢!

Unity编辑器扩展之Hierarchy面板扩展


|-----------------------------------------------------|
| TechX 坚持将创新的科技带给世界! 拥有更好的学习体验 ------ 不断努力,不断进步,不断探索 |

|-----------------------------------------------------------------|
| TechX ------ 心探索、心进取! 助力快速掌握 Hierarchy 面板扩展 为初学者节省宝贵的学习时间,避免困惑! |


文章目录


一、Hierarchy菜单扩展

1、拓展菜单(GameObject)

通过 MenuItem 属性,可以在Hierarchy窗口上下文菜单中将菜单项添加到"GameObject/"菜单。

MenuItem 属性能够将任何静态函数转变为菜单命令。仅静态函数可使用 MenuItem 属性。

之前已经写过相关文章:
https://blog.csdn.net/caiprogram123/article/details/133953622#6GameObject_593

2、GenericMenu自定义菜单扩展

当右键点击 Hierarchy 视图中的 GameObject 时,将创建一个包含 "Option 1" 和 "Option 2" 两个选项的 GenericMenu 菜单,并在点击时触发相应的方法。

之前已经写过相关文章:
https://blog.csdn.net/caiprogram123/article/details/135373693#Hierarchy_172


二、EditorApplication.hierarchyChanged 之自动重命名重复对象名称

EditorApplication.hierarchyChanged 是 Unity 编辑器中的一个事件,用于检测和响应场景层级(Hierarchy)中的变化。

当场景中的对象被添加、删除、重命名,或对象的父子关系发生变化时,Unity 会触发 hierarchyChanged 事件。

这使得开发者能够在这些变化发生时执行自定义逻辑,如自动重命名对象、更新层级视图中的图标或状态、同步层级结构与外部数据等。

csharp 复制代码
using UnityEditor;
using UnityEngine;
using System.Linq;

public class AutoRenameDuplicateObjects
{
    [InitializeOnLoadMethod]
    static void Initialize()
    {
        EditorApplication.hierarchyChanged += OnHierarchyChanged;
    }

    private static void OnHierarchyChanged()
    {
        // 查找场景中的所有对象
        GameObject[] allObjects = Object.FindObjectsOfType<GameObject>();

        // 按照父对象分组,检查每个组中是否有重复名称的对象
        foreach (var parentGroup in allObjects.GroupBy(obj => obj.transform.parent))
        {
        	if(parentGroup.Count() < 2) continue;
        	
            // 对同一层级的对象按照Hierarchy中的顺序排序
			var sortedGroup = parentGroup.OrderBy(obj => obj.transform.GetSiblingIndex()).ToList();

			var duplicates = sortedGroup.GroupBy(obj => obj.name)
                            .Where(group => group.Count() > 1);
            foreach (var duplicateGroup in duplicates)
            {
                int index = 1;
                foreach (GameObject go in duplicateGroup)
                {
                    string newName = $"{go.name}_{index++}";
                    Undo.RecordObject(go, "Auto Rename Duplicate Objects");
                    go.name = newName;
                    EditorUtility.SetDirty(go);
                    Debug.Log($"Renamed {go.name} to {newName}");
                }
            }
        }
    }
}

通过监听 EditorApplication.hierarchyChanged 事件,当层级结构发生变化时,代码会自动检查是否有同一父对象下的子对象名称重复,并按照从上到下的顺序进行重命名。


三、EditorApplication.hierarchyWindowItemOnGUI 之布局扩展

EditorApplication.hierarchyWindowItemOnGUI 是 Unity 编辑器提供的一个事件,用于在 Hierarchy 窗口中绘制自定义的 GUI。

这个事件会在每一帧渲染 Hierarchy 窗口时触发,并为每个可见的层级对象调用一次,允许开发者在 Hierarchy 窗口的每个对象旁边绘制自定义内容,如按钮、图标或文本。

csharp 复制代码
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using System.Reflection;
using System.Linq;

public class HierarchyEditor
{
    private const float MinWindowWidth = 240f; // 设置显示图标和Toggle的最小窗口宽度

    // 在加载时初始化
    [InitializeOnLoadMethod]
    static void HierarchyExtensionIcon()
    {
        var activeStyle = new GUIStyle() { normal = { textColor = Color.green } };
        var inactiveStyle = new GUIStyle() { normal = { textColor = new Color(0, 1, 0, 0.5F) } };

        EditorApplication.hierarchyWindowItemOnGUI += (int instanceID, Rect selectionRect) =>
        {
            GameObject go = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
            if (go == null) return;

            int index = 0;
            //绘制对象激活状态切换按钮
            DrawActiveToggle(go, selectionRect, ref index);
            //绘制静态标记
            DrawStatic(go, selectionRect, ref index);
            //绘制组件ICON
            DrawRectIcon<BoxCollider>(go, selectionRect, ref index);
            //重绘对象名称
            DrawGameObjectName(go, selectionRect, activeStyle, inactiveStyle);
        };
    }

    // 获取 Hierarchy 窗口的宽度
    private static float GetHierarchyWindowWidth()
    {
        PropertyInfo hierarchyInfo = typeof(Editor).Assembly
            .GetType("UnityEditor.SceneHierarchyWindow")
            ?.GetProperty("lastInteractedHierarchyWindow", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);

        EditorWindow hierarchyWindow = (EditorWindow)hierarchyInfo?.GetValue(null);
        return hierarchyWindow?.position.width ?? 0;
    }

    /// <summary>
    /// 获取Rect
    /// </summary>
    private static Rect GetRect(Rect selectionRect, int index)
    {
        Rect rect = new Rect(selectionRect);
        if (GetHierarchyWindowWidth() >= MinWindowWidth)
        {
            rect.x += rect.width - (18 * index);
        }
        else
        {
            rect.x += rect.width + (18 * index);

        }
        rect.width = 18;
        return rect;
    }
}

1、绘制ActiveToggle

为每个对象绘制一个激活状态的 Toggle 按钮,允许用户直接在 Hierarchy 窗口中切换对象的激活状态。

csharp 复制代码
/// <summary>
/// 绘制激活状态的Toggle按钮
/// </summary>
private static void DrawActiveToggle(GameObject go, Rect selectionRect, ref int index)
{
    index++;
    Rect rect = GetRect(selectionRect, index);
    bool currentActiveState = go.activeSelf;

    // 检查鼠标是否在当前对象的 ActiveToggle 区域内
    if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition))
    {
        // 获取选中的所有对象
        GameObject[] selectedObjects = Selection.gameObjects;

        // 如果未选中对象,则只更改当前对象的状态
        if (!selectedObjects.Contains(go))
        {
            Selection.activeGameObject = go;
            selectedObjects = new GameObject[] { go };
        }

        bool newActiveState = !currentActiveState;
        Undo.RecordObjects(selectedObjects, "Toggle Active State");
        foreach (GameObject selectedObject in selectedObjects)
        {
            selectedObject.SetActive(newActiveState);
        }
        EditorSceneManager.MarkAllScenesDirty();
        Event.current.Use(); // 使用事件,避免被传递
    }
    GUI.Toggle(rect, currentActiveState, string.Empty);
}

如果用户改变了对象的激活状态,系统会将所有选中的对象的激活状态同步改变,并标记场景为脏数据(MarkAllScenesDirty),以确保更改会被保存。

2、绘制Static静态标记

如果当前对象是静态的,则在其旁边绘制一个 "S" 标记,以表示该对象被标记为静态

csharp 复制代码
/// <summary>
/// 绘制静态标记
/// </summary>
private static void DrawStatic(GameObject go, Rect selectionRect, ref int index)
{
    if (go.isStatic)
    {
        index++;
        Rect rect = GetRect(selectionRect, index);
        GUI.Label(rect, "S");
    }
}

使用 GUI.Label 方法在计算出的位置上绘制 "S" 标记,以标识静态对象。

3、绘制组件的Icon

如果对象具有特定的组件(例如 BoxCollider),则在该对象的 Hierarchy 列表项旁边绘制一个图标。

csharp 复制代码
/// <summary>
/// 绘制组件的Icon
/// </summary>
private static void DrawRectIcon<T>(GameObject go, Rect selectionRect, ref int index) where T : Component
{
    if (go.GetComponent<T>() != null)
    {
        index++;
        Rect rect = GetRect(selectionRect, index);
        DrawIcon<T>(rect);
    }
}

/// <summary>
/// 绘制Unity原生Icon
/// </summary>
private static void DrawIcon<T>(Rect rect)
{
    var icon = EditorGUIUtility.ObjectContent(null, typeof(T)).image;
    GUI.Label(rect, icon);
}

该方法检查当前对象是否具有 BoxCollider 组件。

如果对象包含该组件,调用 DrawIcon 方法在计算好的矩形区域中绘制组件的图标。

4、重绘对象名称

根据对象的激活状态,以不同的颜色在 Hierarchy 窗口中显示对象的名称。

csharp 复制代码
/// <summary>
/// 绘制对象名称
/// </summary>
private static void DrawGameObjectName(GameObject go, Rect selectionRect, GUIStyle activeStyle, GUIStyle inactiveStyle)
{
    selectionRect.x += 18;
    GUIStyle style = go.activeSelf ? activeStyle : inactiveStyle;
    if (PrefabUtility.IsPartOfAnyPrefab(go)) return;
    GUI.Label(selectionRect, go.name, style);
}

activeStyle 和 inActiveStyle 是用于绘制对象名称的 GUIStyle,分别用于激活状态和非激活状态的对象。

PrefabUtility.IsPartOfAnyPrefab(go) 检查对象是否属于某个预制体(Prefab)。如果对象是预制体的一部分,不执行此绘制,以避免重复显示预制体的名称。


|-----------------------------------------------|
| TechX ------ 心探索、心进取! 每一次跌倒都是一次成长 每一次努力都是一次进步 |


END 感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!

相关推荐
向宇it4 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
为什么每天的风都这么大6 小时前
Vscode/Code-server无网环境安装通义灵码
ide·vscode·阿里云·编辑器·ai编程·code-server
Heaphaestus,RC6 小时前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
芋芋qwq6 小时前
Unity UI射线检测 道具拖拽
ui·unity·游戏引擎
tealcwu6 小时前
【Unity服务】关于Unity LevelPlay的基本情况
unity·游戏引擎
大眼睛姑娘9 小时前
Unity3d场景童话梦幻卡通Q版城镇建筑植物山石3D模型游戏美术素材
unity·游戏美术
鹿野素材屋13 小时前
Unity Dots下的动画合批工具:GPU ECS Animation Baker
unity·游戏引擎
陌上阳光14 小时前
vscode连接远程开发机报错
ide·vscode·编辑器
羊子雄起15 小时前
CKEditor前端样式和编辑器的样式不一致的问题
前端·编辑器
界面开发小八哥18 小时前
「Java EE开发指南」如何使用Visual JSF编辑器设计JSP?(一)
java·ide·java-ee·编辑器·myeclipse