4.2 如何扩扩展默认的编辑器窗口
4.2.1 扩展Hierarchy窗口
csharp
using UnityEditor;
using UnityEngine;
// 自定义Hierarchy窗口扩展类
public class CustomHierarchyWindow
{
// InitializeOnLoadMethod特性表示这个方法在Unity编辑器启动或重新编译后自动执行
[InitializeOnLoadMethod]
static void InitializeOnLoad()
{
// 安全移除旧的事件监听器(防止重复注册)
EditorApplication.hierarchyWindowItemOnGUI
-= OnHierarchyWindowItemGUI;
// 注册新的事件监听器
EditorApplication.hierarchyWindowItemOnGUI
+= OnHierarchyWindowItemGUI;
}
// Hierarchy窗口中每个项目的GUI绘制回调函数
private static void OnHierarchyWindowItemGUI(
int instanceID, // Unity对象的唯一实例ID
Rect selectionRect) // 当前项在Hierarchy窗口中的绘制矩形
{
// 通过实例ID查找对应的游戏对象
GameObject go = EditorUtility
.InstanceIDToObject(instanceID) as GameObject;
// 如果找不到对应的游戏对象则退出
if (go == null)
return;
// 获取该游戏对象上的所有组件
Component[] components = go.GetComponents<Component>();
// 遍历所有组件
for (int i = 0; i < components.Length; i++)
{
Component component = components[i];
// 如果组件丢失(如脚本被删除),则跳过
if (component == null)
continue;
// 获取组件的图标:
// 1. 先尝试获取该类型的内置小图标
// 2. 如果没有,则获取组件的缩略图
Texture texture = AssetPreview.GetMiniTypeThumbnail(
component.GetType()) ??
AssetPreview.GetMiniThumbnail(component);
// 如果没有可用的图标,则跳过
if (texture == null)
continue;
// 计算图标位置(从Hierarchy项右侧从右向左排列)
Rect rect = selectionRect;
rect.x += selectionRect.width - (i + 1) * 20f; // 每个图标间隔20像素
rect.width = 20f; // 图标宽度固定为20像素
// 绘制组件图标并设置工具提示为组件类型名称
GUI.Label(rect, new GUIContent(texture,
component.GetType().Name));
}
}
}
代码功能详解
1. 初始化机制
csharp
[InitializeOnLoadMethod]
static void InitializeOnLoad()
{
// 安全移除旧的事件监听(防止多次注册)
EditorApplication.hierarchyWindowItemOnGUI -= OnHierarchyWindowItemGUI;
// 注册新的事件监听
EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyWindowItemGUI;
}
[InitializeOnLoadMethod]
特性 :使方法在以下情况自动执行:- Unity 编辑器启动时
- 脚本重新编译后
- 事件注册 :订阅
hierarchyWindowItemOnGUI
事件- 该事件在 Hierarchy 窗口绘制每个项目时触发
- 安全移除旧监听防止重复注册
2. 核心绘制函数
csharp
private static void OnHierarchyWindowItemGUI(
int instanceID, // Unity内部对象标识符
Rect selectionRect) // 当前项的矩形位置
{
// 1. 从ID获取实际游戏对象
GameObject go = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
// 2. 有效性检查
if (go == null) return;
// 3. 获取对象的所有组件
Component[] components = go.GetComponents<Component>();
// 4. 遍历所有组件
for (int i = 0; i < components.Length; i++)
{
Component component = components[i];
// 跳过丢失的脚本(组件为空)
if (component == null) continue;
// 5. 获取组件图标(详见下文)
Texture texture = GetComponentIcon(component);
if (texture == null) continue;
// 6. 计算位置并绘制图标
Rect iconRect = CalculateIconRect(selectionRect, i);
GUI.Label(iconRect, new GUIContent(texture, component.GetType().Name));
}
}
3. 图标获取逻辑
csharp
Texture GetComponentIcon(Component component)
{
// 1. 先获取类型专用图标(如Camera、Light等内置图标)
Texture typeIcon = AssetPreview.GetMiniTypeThumbnail(component.GetType());
// 2. 如果没有类型图标,获取组件的缩略图(自定义脚本图标)
if (typeIcon == null)
{
typeIcon = AssetPreview.GetMiniThumbnail(component);
}
return typeIcon;
}
4. 图标位置计算
csharp
Rect CalculateIconRect(Rect baseRect, int index)
{
Rect rect = baseRect;
// 从右侧开始排列
// - 20px * (index + 1) 确保从左向右顺序排列
rect.x += baseRect.width - (index + 1) * 20f;
// 固定宽度20像素
rect.width = 20f;
return rect;
}
5. 图标显示效果
在 Hierarchy 窗口中显示:
[游戏对象名称] [图标3] [图标2] [图标1]
- 图标顺序:组件添加顺序从右向左排列
- 悬停提示:鼠标悬停时显示组件类型名
- 自动跳过 :
- 丢失的脚本(组件为null)
- 没有可用图标的组件
使用效果示例
场景中的显示效果:

用处
-
组件可视化:
- 直观展示对象上所有组件
- 无需展开查看Inspector
-
脚本丢失检测:
- 脚本丢失时图标不显示
- 快速发现损坏的对象
-
快速识别:
- 特殊图标表示特定组件
- 悬停提示显示具体类型名
-
顺序指示:
- 图标排列顺序反映组件添加顺序
- 帮助理解对象结构
使用注意事项
-
性能影响:
- 大型场景中可能会影响编辑器性能
- 建议添加显示对象数量阈值
-
兼容性:
- 只兼容 2018.3+ 版本的 Unity
- 旧版本 Unity 需要使用其他方法