当前位置: 首页 > news >正文

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

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

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

相关文章:

  • 数美Android SDK
  • 【412】【统计近似相等数对 I】
  • docker python 3.11 容器报错
  • [M模拟] lc3153. 所有数对中数位不同之和(模拟+按位统计)
  • golang 多版本管理工具
  • IDEA没有SQL语句提示
  • 【Springboot服务实现类】用户登录逻辑梳理(未完待续)
  • 数据库与缓存一致性的解决方案
  • django(一):项目搭建
  • python-pptx - Python 操作 PPT 幻灯片
  • Node.js中离线集成ip2region:实现高效的IP地址定位
  • 【网络安全】服务基础第一阶段——第六节:Windows系统管理基础---- DNS部署与安全
  • 单片机的主流编程语言是什么
  • Vue的计算属性:methods方法、computed计算属性、watch监听属性
  • 拿到一个新项目,如何开展测试?
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • css属性的继承、初识值、计算值、当前值、应用值
  • JavaScript HTML DOM
  • Javascript Math对象和Date对象常用方法详解
  • JS函数式编程 数组部分风格 ES6版
  • laravel with 查询列表限制条数
  • Mac转Windows的拯救指南
  • Mocha测试初探
  • Otto开发初探——微服务依赖管理新利器
  • Protobuf3语言指南
  • Redash本地开发环境搭建
  • Redis学习笔记 - pipline(流水线、管道)
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • spring boot 整合mybatis 无法输出sql的问题
  • underscore源码剖析之整体架构
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 使用parted解决大于2T的磁盘分区
  • 我与Jetbrains的这些年
  • HanLP分词命名实体提取详解
  • postgresql行列转换函数
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ​​​​​​​STM32通过SPI硬件读写W25Q64
  • # 学号 2017-2018-20172309 《程序设计与数据结构》实验三报告
  • #Datawhale AI夏令营第4期#AIGC文生图方向复盘
  • (day18) leetcode 204.计数质数
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (solr系列:一)使用tomcat部署solr服务
  • (二) 初入MySQL 【数据库管理】
  • (二)测试工具
  • (二十三)Flask之高频面试点
  • (接上一篇)前端弄一个变量实现点击次数在前端页面实时更新
  • (蓝桥杯每日一题)love
  • (十八)Flink CEP 详解
  • (小白学Java)Java简介和基本配置
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET CLR基本术语
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • @Async 异步注解使用