Unity里有个窗口,几乎每个人从第一天打开编辑器就在用,但真正把它摸透的人不多——它就是Hierarchy视图。很多人对它的认知停留在"放场景对象的地方",选中、改名、删掉,完事。可只要项目规模一上来,场景里几百上千个GameObject堆在一起,找东西全靠肉眼扫、靠Ctrl+F碰运气,这时候才会意识到:Hierarchy根本不是"一个列表",它是整个场景数据结构在编辑器里的一层映射,你在这上面的每一个操作,都会直接落到Transform的父子关系和GameObject的激活状态上。
这篇文章面向的是已经能跑通Unity基础流程、但还没系统梳理过Hierarchy的开发者,也适合那些场景越来越大、开始被组织方式拖累效率的人。下面我不按"菜单逐个翻译"的路子讲,而是把它当成一套日常工具,说说每一块到底在解决什么问题、有哪些坑、怎么用编辑器扩展把它改造成顺手的形状。
1. Hierarchy窗口背后管的是GameObject和Transform的树
1.1 你在窗口里看到的那一行,真实身份是什么
Hierarchy里每一行对应场景里一个GameObject,缩进关系对应Transform的父子关系。这点说起来简单,但它决定了很多行为。比如你在Hierarchy里把一个对象拖到另一个对象下面,本质上改的是transform.SetParent(...),被拖的那个对象的localPosition、localRotation、localScale会按父级的变换重新计算,而不是保持世界坐标不变。这一点新手最容易栽跟头:把场景根节点下的一个模型拖进一个已旋转、已缩放的父物体里,模型位置当场就跑偏了,还以为是自己点错了。
提示:在Scene视图里按住V键拖动顶点时,Unity会自动吸附到其他对象的顶点,配合Hierarchy拖拽能减少位置错乱;但真正稳妥的做法是拖拽后检查Inspector里的Transform数值,或者用
transform.SetParent(parent, true)保留世界坐标的思路在脚本里处理。
一个GameObject在没有Transform时能不能存在于场景里?答案是不能。场景里所有对象都挂Transform(UI元素是RectTransform,它继承自Transform)。所以Hierarchy本质上展示的是"场景中所有Transform组成的森林",每个根级的GameObject就是一棵树的根。理解这一点之后,你就能想到:Hierarchy里那些看起来像"分组"的空物体,其实也是货真价实的GameObject,只不过除了Transform什么都没挂,它们的作用就是给子对象提供一个统一的变换基准和命名空间。
1.2 缩进、图标、颜色,每一层信息都有含义
Hierarchy里对象的显示信息比大多数人以为的多:
- 左侧三角形:展开/折叠子对象。按住Alt再点,会递归展开或折叠这个对象下的所有层级,场景层级深的时候非常省事。
- 名字前的复选框:GameObject的activeSelf状态。取消勾选等价于
SetActive(false),运行时该对象及其所有子对象都会被禁用,脚本的Update、物理、渲染全部停掉。 - 名字左侧的小图标:代表这个对象上挂了哪些组件。一个立方体图标说明有MeshRenderer,一个相机图标说明有Camera组件。扫一眼就能判断对象类型,比逐个点开Inspector快得多。
- 名字文字颜色:白色是普通对象;蓝色文字代表这个对象是Prefab实例;灰色(半透明)通常是被Scene Visibility隐藏,或者本身inactive。
这里有个很多人搞混的地方,正好回应一个高频问题:"UI显示隐藏到底该用SetActive、改localScale还是移出相机范围?"Hierarchy里那个复选框就是SetActive的可视化入口,它和改localScale有本质区别。SetActive(false)会停掉这个对象上所有MonoBehaviour的生命周期方法,动画、协程、协程里的计时全部暂停;而把scale设成0只是渲染上看不见,脚本还在跑、碰撞体还在、协程还在走。所以如果只是想临时隐藏某个面板,两种方式都行;但如果这个面板上有持续计时的逻辑,你并不希望它停,那用scale或CanvasGroup.alpha更合适。至于"移出相机",那是纯渲染层面的hack,不到万不得已别用,维护成本太高。
2. 右键菜单和拖拽:创建对象的效率与陷阱
2.1 创建菜单的分类逻辑
在Hierarchy空白处右键,会看到一个很长的创建菜单,大致分几类:
- Create Empty:创建一个只有Transform的空对象。这是做分组、挂脚本逻辑的首选。很多人import一堆模型之后直接平铺在根级,其实应该先Create Empty建一个容器,把相关对象塞进去。
- Create Empty Child:直接在当前选中对象下创建空子对象,省掉一次拖拽。
- 3D Object / 2D Object:内置的基本几何体和精灵。
- UI:Canvas、Panel、Button、Text等。注意这里创建UI时,如果场景里没有Canvas,Unity会自动帮你建一个Canvas和EventSystem。EventSystem只有一个,重复建会导致输入事件混乱。
- Effects、Light、Audio、Video、Camera等各自成一组。
从效率角度,我个人的习惯是:进入一个新场景先建一个__Environment空对象和一个__Systems空对象,把静态场景内容和运行时逻辑对象分开。名字前面加下划线是为了让它排在Hierarchy顶部,因为Unity默认按名称做字母排序时可以这样控制顺序。
2.2 拖拽里的几个细节,文档不会告诉你
拖拽是Hierarchy最高频的操作,但细节坑不少:
| 操作 | 结果 | 易踩的坑 |
|---|---|---|
| 拖到对象上松开 | 成为其子对象 | 位置按父级变换重算,世界坐标可能变 |
| 拖到空白处 | 移到场景根级 | 世界坐标保留,但localPosition会变 |
| 按住Alt拖拽 | 放到鼠标所在的精确位置 | 常用于零散摆放,但容易放错父级 |
| 从Project拖Prefab进来 | 实例化Prefab | 拖到某个对象上会直接成为其子级 |
| Ctrl+D复制 | 复制选中对象 | 复制出的对象会带"(1)"后缀,且挂在同一父级下 |
一个高频翻车现场:把一个Prefab实例拖到另一个Prefab实例下面,结果产生了Prefab嵌套。Unity从2018.3之后支持Prefab嵌套,但嵌套Prefab的覆盖(Override)和Apply逻辑会变得复杂。如果你只是想在运行时临时组合两个Prefab,用脚本Instantiate再SetParent更清晰;如果在编辑期就嵌套,之后改动内层Prefab,外层实例的覆盖状态可能和你想的不一样。
注意:Hierarchy里拖拽Prefab实例时,如果目标父对象是场景对象(非Prefab),实例会变成"场景对象+Prefab实例"的混合结构,此时这个实例的Transform覆盖不会被记录回Prefab资产,这是符合预期的,不要试图Apply。
另外,热词里提到的"背包物品拖拽"是UI层面的拖拽(实现IDragHandler),和Hierarchy的拖拽完全不是一回事,别混淆。Hierarchy拖拽是编辑器行为,运行时不存在;背包拖拽是运行时的输入事件。
3. 搜索、过滤和可见性:大场景里的定位手段
3.1 搜索框的语法远不止输名字
Hierarchy顶部的搜索框支持一套查询语法,用好了能省下大量滚动时间:
- 直接输入名字:模糊匹配,大小写不敏感。
t:按类型搜索:比如t:Light列出所有灯光,t:MeshRenderer列出所有带MeshRenderer的对象,t:Canvas列出所有Canvas。还能搜组件而非GameObject,比如t:AudioSource能筛出所有音频源。l:按标签搜索:l:Player筛出所有打了Player标签的对象。- 组合搜索:
t:Light l:Main这种组合是支持的。还可以直接搜组件里的字段值,比如输入"Health"能找到挂了Health脚本的对象。
搜索会实时过滤Hierarchy的显示,但注意:搜索只是改变显示,不改变场景结构。过滤状态下拖拽对象要格外小心,因为你看到的层级是残缺的,父级可能被隐藏了。我踩过一次坑:搜索t:Enemy过滤后选中一批敌人,想批量拖到一个容器下,结果容器本身不匹配搜索没显示,拖拽落点全靠蒙。
3.2 Scene Visibility和Active是两个维度的事
Hierarchy右上角有个小眼睛图标,点开会切换Scene Visibility模式。这是一套独立于GameObject active状态的编辑器可见性系统:
- 眼睛图标控制的是编辑器里看不看得见,运行时完全不受影响,导出构建后也不影响。
- 名字前的复选框控制的是GameObject的真实激活状态,运行时生效。
- 按H键可以切换选中对象的Scene Visibility,Shift+H取消所有隐藏。
这两个机制分开之后,"Unity不用脚本在项目里隐藏部分组件"这个需求就有了两种答案:如果只是想编辑时不被某块装饰物挡住视线,用Scene Visibility(眼睛图标);如果是想让某个对象运行时彻底不参与,用SetActive。更进一步,如果你只想隐藏对象上的某些组件而不是整个对象,Hierarchy是做不到的——Hierarchy粒度只到GameObject。要隐藏组件,得在Inspector里禁用组件(组件右上角的小勾),或者用编辑器扩展在Hierarchy上做定制显示。
这个区分在调试时特别有用:比如一个复杂的粒子特效挡住了主角,用Scene Visibility隐藏它,运行起来它照样播;如果用SetActive关掉,运行前还得记得打开。
4. Prefab在Hierarchy里的表现和覆盖机制
4.1 蓝色图标和加号、箭头分别代表什么
Prefab实例在Hierarchy里有专门的视觉标记:
- 蓝色立方体图标:普通Prefab实例。
- 蓝色文字:这个实例相对源Prefab有属性覆盖(Override)。
- 名字右边的加号(+):这个实例比源Prefab多了子对象(新增的层级也是覆盖的一种)。
- 名字右边的小箭头(>):点击可以展开一个面板,列出这个实例所有覆盖项,方便逐个Revert。
理解这套标记的价值在于:团队协作时,你在Hierarchy扫一眼颜色和符号,就能判断哪些实例是"干净"的、哪些改过。改过的实例如果Apply错了方向,会把本地的临时改动推给所有人。我的原则是:除非这个修改确实应该成为所有实例的共同行为,否则绝不点Apply。大多数时候,实例级的改动就应该留在实例上。
4.2 覆盖的三种类型和它们的优先级
覆盖分三类,处理顺序不一样:
- 属性覆盖:改了Inspector里某个值的实例。Revert时恢复成Prefab资产的值。
- 新增对象覆盖:在实例下加了新的子GameObject。Revert会删掉这个子对象,用之前想清楚。
- 删除对象覆盖:删掉了源Prefab里有的子对象。Revert会把子对象加回来。
嵌套Prefab的覆盖更绕:外层实例的覆盖点和内层Prefab的覆盖点可能相互影响。遇到嵌套Prefab改不动的情况,先在Hierarchy里点开那个小箭头面板,它能清楚列出所有覆盖,比在Inspector里翻半天直观得多。
提示:如果你在Hierarchy里右键一个Prefab实例,选择"Unpack Prefab Completely",这个实例会彻底变成普通GameObject,之后就不再和源Prefab有任何关联。这个操作不可逆,做之前确认清楚。还有"Unpack Prefab"只解开最外层,保留内层嵌套Prefab。
5. 大规模场景下的Hierarchy组织和性能
5.1 命名规范和分组策略决定你后期找东西的速度
场景对象超过一两百个之后,组织方式的影响就非常明显了。我总结了几条实践:
- 用空对象做逻辑分组,而不是靠位置或注释。比如
Player、Enemies、Environment、UI各占一个根节点。 - 命名带前缀便于排序和搜索,比如所有特效以
FX_开头。Unity的搜索框支持这种前缀,输入FX_就能筛出全部特效。 - 不要频繁重命名根级对象,脚本里如果有通过名字
GameObject.Find的引用,改名会直接断掉。Find本身就慢且脆弱,能不用就不用,用序列化引用或者标签代替。 - 给关键的运行时对象打Tag和Layer,Hierarchy里选中对象后Tag会显示在Inspector顶部,配合
l:搜索定位很快。
热词里"Unity不用脚本在项目里隐藏部分组件"其实也可以换个思路理解:很多"隐藏"需求是组织需求,而不是运行时需求。把不用的装饰性对象收进一个折叠的空对象里,比真去隐藏它更省事,也不影响任何逻辑。
5.2 Hierarchy本身也会拖慢编辑器
场景对象极多时,Hierarchy窗口的展开、重绘会占用编辑器的主线程时间。几个实测有效的习惯:
- 尽量折叠不需要看的层级,别让几千个节点全部展开。
- 用搜索代替滚动,减少重绘范围。
- 关掉"Show Only Selected"以外的额外显示选项(Hierarchy右上角的菜单),视觉噪音少了编辑器也轻快些。
- 如果项目里有大量动态生成的对象,考虑运行时把它们挂在一个统一的容器下,并在不需要时销毁,而不是留一堆空壳在场景里。
另外,"t:Material"这类搜索如果对象特别多,会有一小段卡顿,属于正常现象,别在动画播放高峰时做全场景类型搜索。
6. 用编辑器扩展把Hierarchy改造成自己的形状
6.1 hierarchyWindowItemOnGUI:在Hierarchy行上画东西
EditorApplication.hierarchyWindowItemOnGUI是一个静态事件,可以让你在Hierarchy每一行绘制自定义内容。典型用法是给特定组件或标签的对象行加背景色、加图标、加小按钮。写这类扩展需要注意:
- 事件签名是
(int instanceID, Rect selectionRect),selectionRect是这一行的矩形区域,你可以在它上面画。 - 绘制前用
Event.current.type == EventType.Repaint判断,避免在非重绘时机乱画。 - 通过
EditorUtility.InstanceIDToObject或EditorUtility.EntityIdToObject(Unity 2022+推荐后者)拿到对应的GameObject。 - 一定记得在OnDisable里把事件减掉,否则域重载后会重复注册。
这类扩展必须是编辑器脚本,放在名为Editor的文件夹下,且不能被构建包引用。
6.2 常见扩展场景和调试技巧
几个我实际用过的方向:
- 给带特定脚本的对象行加背景色,一眼看出哪些对象有逻辑。
- 给被打过标签的对象加小标记,替代在Inspector翻标签。
- 在行右侧加小按钮,一键选中该对象的所有同类型兄弟节点。
调试这类扩展的坑:hierarchyWindowItemOnGUI触发的频率很高,任何在里面做重计算的代码都会拖慢整个编辑器。不要在回调里遍历整个场景、不要查数据库、不要做文件IO。还有GUI的坐标是相对Hierarchy窗口的,缩放和DPI变化时布局会变,画出来的效果可能不如预期,最好用GUI.skin里现成的样式,而不是自己硬编码坐标。
如果不想写扩展,Unity自带的Hierarchy右键菜单里也有几个好用的小功能:Copy Path能复制对象的完整层级路径(调试时汇报问题特别方便),Sorting里的Alphabetical Sort和Transform Sort能一键整理同级对象的顺序。Alphabetical按名字排,适合命名规范的项目;Transform Sort按子对象索引排,可以恢复被手动打乱过的顺序。
7. 几个日常操作里容易忽略的快捷键
最后说几个能明显提速的快捷键,都属于用一次就回不去的:
- F键:选中Hierarchy里的对象,鼠标移到Scene视图按F,视图聚焦到该对象。反过来在Scene视图选中对象,Hierarchy里也会高亮。
- Alt+点击折叠三角:递归展开或折叠整个子树。
- Shift/Ctrl多选:和文件管理器一致,但注意多选状态下拖拽会保持相对层级,别指望它们之间自动重新排序。
- Ctrl+Shift+N:在当前选中对象下创建空子对象(等价于右键Create Empty Child)。
- 在Hierarchy里把一个对象拖到另一个上,但按住不放移动:可以精确控制落点,松开前会显示插入指示线。
我个人在实际操作中的一个体会是:Hierarchy用得顺不顺,核心不在于背下多少菜单项,而在于你脑子里有没有一张"场景结构图"。命名、分组、Prefab策略、可见性这几个维度想清楚了,Hierarchy自然会变得清爽,找对象、改配置、排查问题的时间都会明显缩短。而当你发现某些重复操作每天都在做,那就是该写一个编辑器扩展把它自动化掉的时候了。