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

news2024/9/27 19:16:20

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

Unity编辑器扩展之Hierarchy面板扩展
     
TechX 坚持将创新的科技带给世界!

拥有更好的学习体验 —— 不断努力,不断进步,不断探索
TechX —— 心探索、心进取!

助力快速掌握 Hierarchy 面板扩展

为初学者节省宝贵的学习时间,避免困惑!


文章目录

  • 一、Hierarchy菜单扩展
    • 1、拓展菜单(GameObject)
    • 2、GenericMenu自定义菜单扩展
  • 二、EditorApplication.hierarchyChanged 之自动重命名重复对象名称
  • 三、EditorApplication.hierarchyWindowItemOnGUI 之布局扩展
    • 1、绘制ActiveToggle
    • 2、绘制Static静态标记
    • 3、绘制组件的Icon
    • 4、重绘对象名称


一、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 事件。

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

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 窗口的每个对象旁边绘制自定义内容,如按钮、图标或文本。

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 窗口中切换对象的激活状态。

/// <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” 标记,以表示该对象被标记为静态

/// <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 列表项旁边绘制一个图标。

/// <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 窗口中显示对象的名称。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2089296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

css-functions-图形函数

概述 本文会讨论和图形函数有关的 5 个函数,分别是:inset、circle、ellipse、polygon和path。这个 5 个图形函数也对应着相应的<basic-shape>。 <basic-shape>是一种表现基础图形的 CSS 数据类型,作用于clip-path或shape-outside属性中;而其值是由图形函数获…

sfud移植

sfud移植 首先看readme文档 文件结构 inc文件夹&#xff1a;各种头文件&#xff0c;注意flash_def和cfg头文件 port文件夹&#xff1a;接口文件 src文件夹&#xff1a;代码源文件 移植 基础&#xff1a;你的SPI没问题&#xff0c;用普通工程可以正常操作Flash 首先打开f…

cloudways相关

服务器优惠链接&#xff1a; https://www.cloudways.com/en/?id1081165 cloudways 后台清缓存位置&#xff1a; 网站迁移到cloudways&#xff08;用cloudways的自动迁移插件&#xff09;&#xff1a; 不管原网站是不是用的cloudways主机&#xff0c;都可以用这个方法。 1…

【React】react项目安装tailwindcss

创建React项目 首先,如果您还没有React项目,可以使用以下命令创建一个新项目: npx create-react-app my-tailwind-app cd my-tailwind-app安装Tailwind CSS 接下来,按照以下步骤安装Tailwind CSS: 安装必要的依赖: npm install -D tailwindcss postcss autoprefixer初始化…

Kubernetes 1.31 新功能: 细粒度补充组控制

这篇文章讨论了 Kubernetes 1.31 中的一个新特性&#xff0c;用于改善 Pod 中容器的补充组&#xff08;Fine-grained SupplementalGroups control&#xff09;处理。 动机&#xff1a;在容器镜像中的 /etc/group 定义的隐式组成员身份 尽管这种行为可能并不受许多 Kubernetes…

Redis安装+常用命令合集大全+Redis Desktop Manager

文章目录 一&#xff1a;Redis 简介二&#xff1a;安装和配置Redis第一步&#xff1a;下载Redis第二步&#xff1a;解压Redis第三步&#xff1a;配置Redis第四步&#xff1a;启动Redis服务器第五步&#xff1a;验证Redis安装第六步&#xff1a;设置Redis为全局命令 三&#xff…

不同品类商标一样属于侵权吗!

商标分类有45类&#xff0c;有网友问普推知产商标老杨&#xff0c;不同品类商标一样属于侵权吗&#xff0c;这个要从多个角度来分析&#xff0c;不同品类商标是相同一样的&#xff0c;这样的基本不侵权的。 在注册申请商标是会经常遇到别人在某类别注册&#xff0c;但是有一些类…

[Meachines] [Insane] Bankrobber XSS-MDOG+SQLI+XSRF+Local-RCE+Bankv2转账模拟应用缓冲区溢出

信息收集 IP AddressOpening Ports10.10.10.154TCP:80&#xff0c;443&#xff0c;445&#xff0c;3306 $ nmap -p- 10.10.10.154 --min-rate 1000 -sC -sV -Pn PORT STATE SERVICE VERSION 80/tcp open http …

各个击破:NetXpert XG2帮您解决“布线安装与维护”难题

在传输大量数据时&#xff0c;光纤变得越来越重要&#xff0c;而铜缆在未来也将继续发挥重要作用&#xff0c;因此我们不仅要比较两种类型布线的优缺点&#xff0c;还要探究光纤传输中的错误来源。 测试光缆传输损耗的准确性对于故障排除至关重要&#xff0c;特别是在光纤情况下…

hadoop技术

历史版本 HA&#xff1a;高可用 技术框架

[线程]线程不安全问题 --- 死锁

文章目录 一. 引出死锁二. 可重用锁三. 死锁的三种典型场景四. 死锁产生的四个必要条件(面试题)1. 锁具有互斥特性2. 锁不可抢占(不可被剥夺)3. 请求和保持4. 循环等待 五. 避免死锁问题 一. 引出死锁 class Counter{private int count;public void add(){synchronized(this){…

深度学习语义分割篇——DeeplabV3原理详解+源码实战

&#x1f34a;作者简介&#xff1a;秃头小苏&#xff0c;致力于用最通俗的语言描述问题 &#x1f34a;专栏推荐&#xff1a;深度学习网络原理与实战 &#x1f34a;近期目标&#xff1a;写好专栏的每一篇文章 &#x1f34a;支持小苏&#xff1a;点赞&#x1f44d;&#x1f3fc;、…

域内安全:委派攻击

目录 域委派 非約束性委派攻击&#xff1a; 主动访问&#xff1a; 被动访问&#xff08;利用打印机漏洞&#xff09; 约束性委派攻击&#xff1a; 域委派 域委派是指将域内用户的权限委派给服务账户&#xff0c;使得服务账号能够以用户的权限在域内展开活动。 委派是域中…

机器学习——决策树模型

决策树原理 算法概述 从根节点开始一步步走到叶子节点&#xff08;决策&#xff09; 所有数据最终都会落到叶子节点&#xff0c;既可以做分类也可以做回归 例如上例&#xff0c;输入一个数据后&#xff0c;先判断他的年龄&#xff0c;然后再判断性别 在决策树中&#xff0c…

day45.动态规划

1035.不相交的线: 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足&#xff1a; nums1[i] nums2[j] 且绘制的直线不与任何其他连线&#xff08;非水…

基站定位系统的创新应用:企业管理的新利器

在现代企业的管理中&#xff0c;基站定位系统已经成为不可或缺的技术手段。通过这一系统&#xff0c;企业能够实时掌握物资、人员的位置和状态&#xff0c;提升管理效率和安全性。常达智能物联凭借深厚的技术积累和丰富的项目经验&#xff0c;为各类企业提供了创新的基站定位系…

如何使用ChatGPT,提示词篇之【编程代码】

一、 ChatGPT可以做什么&#xff1f; ChatGPT能做的事情非常多&#xff01;它不仅仅是一个对话AI。以下是一些主要功能&#xff1a; 1. 回答问题&#xff1a;无论是学术问题、技术问题&#xff0c;还是生活琐事&#xff0c;ChatGPT都能提供帮助。 2. 写作助手&#xff1a;可以…

Angular17(3):Angular项目中引入iconfont

在Angular项目中引入Iconfont&#xff08;图标字体&#xff09;是一个常见的需求&#xff0c;用于在应用中添加丰富的图标资源。 Iconfont-阿里巴巴矢量图标库 1、点击进入官网&#xff0c;注册并登录 2、登陆成功后&#xff0c;首页的 资源管理 > 我的项目 点击进入 3、…

网络通信---四次挥手

文章目录 概述四次挥手第一次挥手&#xff1a;第二次挥手&#xff1a;第三次挥手&#xff1a;第四次挥手&#xff1a; 问题&#xff1a;为什么是四次&#xff0c;而不是三次&#xff1f;确保数据传输完成&#xff1a;防止数据丢失&#xff1a;避免旧连接干扰&#xff1a;防止死…

C#的继承

继承是面向对象程序设计中最重要的概念之一.继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易,同时,也有利用重用代码和节省开发时间. 当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数只需要设计一个新的类,继承了已有的类的成员即可…