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

相关推荐
Unity大海1 小时前
诠视科技Unity SDK开发环境配置、项目设置、apk打包。
科技·unity·游戏引擎
浅陌sss6 小时前
Unity中 粒子系统使用整理(一)
unity·游戏引擎
维度攻城狮11 小时前
实现在Unity3D中仿真汽车,而且还能使用ros2控制
python·unity·docker·汽车·ros2·rviz2
为你写首诗ge14 小时前
【Unity网络编程知识】FTP学习
网络·unity
1alisa16 小时前
Sublime Text for Mac v4【注册汉化版】代码编辑器
macos·编辑器·sublime text
神码编程16 小时前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎
菲fay17 小时前
Unity 单例模式写法
unity·单例模式
mzak18 小时前
vscode集成deepseek实现辅助编程(银河麒麟系统)【详细自用版】
linux·vscode·编辑器·银河麒麟·deepseek
一个程序员(●—●)19 小时前
编辑器检视器面板深度扩展2+编辑器菜单栏扩展
编辑器
火一线19 小时前
【Framework-Client系列】UIGenerate介绍
游戏·unity