内容将会持续更新,有错误的地方欢迎指正,谢谢!
拥有更好的学习体验 —— 不断努力,不断进步,不断探索 |
助力快速掌握 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)。如果对象是预制体的一部分,不执行此绘制,以避免重复显示预制体的名称。
每一次跌倒都是一次成长 每一次努力都是一次进步 |
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!