内容将会持续更新,有错误的地方欢迎指正,谢谢!
Unity编辑器扩展之Hierarchy面板扩展
|-----------------------------------------------------|
| TechX 坚持将创新的科技带给世界! 拥有更好的学习体验 ------ 不断努力,不断进步,不断探索 |
|-----------------------------------------------------------------|
| TechX ------ 心探索、心进取! 助力快速掌握 Hierarchy 面板扩展 为初学者节省宝贵的学习时间,避免困惑! |
文章目录
- 一、Hierarchy菜单扩展
- [二、EditorApplication.hierarchyChanged 之自动重命名重复对象名称](#二、EditorApplication.hierarchyChanged 之自动重命名重复对象名称)
- [三、EditorApplication.hierarchyWindowItemOnGUI 之布局扩展](#三、EditorApplication.hierarchyWindowItemOnGUI 之布局扩展)
一、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 感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!