49.总结
49.1 知识点

总结主要内容
更多内容

主要用处

49.2 核心要点速览
自定义菜单栏(MenuItem / AddComponentMenu)
- 使用
UnityEditor的脚本放在 Editor 文件夹下,避免编辑器代码打进包体。 - MenuItem:修饰静态方法;不要求继承
MonoBehaviour;路径分隔符必须是 **/**,不能用\。 - AddComponentMenu:命名空间
UnityEngine,给继承 MonoBehaviour 的脚本出现在 Component 菜单;一般不放在 Editor 文件夹。
| 入口 | MenuItem 路径前缀 | 说明 |
|---|---|---|
| 主菜单自定义项 | 一级/二级/... |
直接写菜单层级 |
| Hierarchy / GameObject 菜单与右键 | GameObject/... |
主菜单 GameObject 里也会出现同名项 |
| Project / Assets 菜单与右键 | Assets/... |
主菜单 Assets 里也会出现同名项 |
| Inspector 组件右键 | CONTEXT/脚本类名/... |
脚本类名与目标组件类名一致 |
- 快捷键:在路径字符串末尾加
_键名;组合键用%Ctrl、**#** Shift、**&** Alt;支持单键与方向键、F1–F12、HOME、END、PGUP、PGDN 等(系列正文已列举)。
自定义窗口(EditorWindow)
继承 EditorWindow,在 OnGUI 里用 GUILayout / EditorGUILayout 画控件;通常由 MenuItem 里调用 GetWindow 取实例,设 titleContent 后 Show。
创建与查询(静态 API)
| API | 作用 | 何时用 |
|---|---|---|
| GetWindow | 获取或创建该类型窗口,同类型常表现为单例 | 大多数工具窗:只保留一个面板 |
| CreateWindow | 创建窗口,同一类型可多个实例 | 要并排多个独立面板、对照编辑 |
| GetWindowWithRect | 指定初始矩形(位置与大小)创建/显示 | 需要固定弹出区域 |
| HasOpenInstances | 判断某 EditorWindow 类型是否已有打开实例 | 「已打开则聚焦、未打开则创建」 |
GetWindow 重载里常见参数:utility 为 true 时偏可自由拖动、缩放的浮动工具窗;为 false 偏普通停靠式窗口;另有 title、focus、desiredDockNextTo(靠 dock 到某类窗口旁)等。
事件回调(与 Hierarchy / Project / 选中 / 焦点 联动)
| 回调 | 何时触发 | 可用来做什么 |
|---|---|---|
| OnHierarchyChange | 场景层次里对象创建、删除、重命名等 | 场景结构变了之后刷新列表、缓存 |
| OnProjectChange | 工程资源增删改 | 资源变了之后刷新路径、依赖显示 |
| OnSelectionChange | 当前选中对象变化 | 窗口内容与 Inspector 选中对象对齐 |
| OnFocus / OnLostFocus | 本窗口获得 / 失去键盘焦点 | 焦点相关状态、暂停昂贵刷新 |
| OnInspectorUpdate | 随 Inspector 刷新节奏调用 | 需要与检视器刷新同步的轻量更新 |
生命周期(窗口自身)
| 回调 | 作用 |
|---|---|
| OnEnable | 窗口激活时做一次初始化(引用、缓存、订阅) |
| OnGUI | 每帧绘制 IMGUI;按钮、布局、即时模式控件都放这里 |
| Update | 编辑器里每帧逻辑更新;与 OnGUI 分工,非绘制类持续逻辑可放这里 |
| OnDestroy | 窗口销毁时收尾、取消订阅 |
实例与静态成员(常用)
| 类别 | 名称 | 作用 |
|---|---|---|
| 静态只读 | focusedWindow、mouseOverWindow | 当前焦点窗口 / 鼠标下的 EditorWindow |
| 实例 | titleContent、position | 窗口标题与屏幕矩形 |
| 实例 | wantsMouseEnterLeaveWindow | 为 true 时鼠标进入/离开窗口会多触发 OnGUI,做悬停高亮等 |
| 实例 | Show、Repaint、Close | 显示、强制重绘、关闭窗口 |
EditorGUI / EditorGUILayout(IMGUI 控件)
- EditorGUI 对标 GUI:按 Rect 手动摆位;含编辑器常用专用控件。
- EditorGUILayout 对标 GUILayout:自动布局,系列正文以该类为主;与 EditorGUI 的差别主要是是否自算位置。
- 拓展里一般在 EditorWindow.OnGUI(或自定义 Inspector 等)里调用;可与 GUI / GUILayout 混用。
布局选项(GUILayoutOption,复习):Width / Height 固定宽高;MinWidth / MaxWidth 等约束;ExpandWidth(true/false) 等控制是否沿水平方向扩展占满。
文本 · 层级 · 标签 · 颜色
| API | 干什么用 |
|---|---|
| LabelField | 只读文本;重载可「左标题 + 右内容」 |
| LayerField | 选 Layer(内部为 int),建议带前缀标签便于辨认 |
| TagField | 选 Tag(string) |
| ColorField | 取 Color;重载可控制拾色器、透明度、HDR 等 |
枚举 · 整数下拉 · 特殊按钮
| API | 干什么用 | 注意 |
|---|---|---|
| EnumPopup | 单选枚举 | 返回值转回枚举类型 |
| EnumFlagsField | 多选(按位或) | 枚举需按位赋值,并配好组合值 |
| IntPopup | 下拉文案用 strs,返回 int 数组 ints 里选中的那个值 |
显示与存储分离 |
| DropdownButton | 鼠标按下当下就返回 true | FocusType.Keyboard 可走 Tab 焦点;Passive 不接收键盘焦点 |
对象引用与各类输入
| 类别 | 作用 |
|---|---|
| ObjectField | 拖对象或选资源;typeof 指定类型;最后一参控制是否允许场景内对象 |
| IntField、LongField、FloatField、DoubleField、TextField | 基本数值与字符串 |
| Vector2/3/4Field、RectField、BoundsField、BoundsIntField | 向量、矩形、包围盒 |
| DelayedIntField 等 Delayed* | 按 Enter 或失焦前不提交新值,减少输入过程中频繁改动 |
折叠与开关
| API | 干什么用 | 和其它的区别 |
|---|---|---|
| Foldout | bool 表示展开;第三参为 true 时点标题整行可切换,false 时只能点箭头 | 轻量折叠行 |
| BeginFoldoutHeaderGroup / EndFoldoutHeaderGroup | 带标题行高亮的折叠区,子控件包在 bool 为 true 的分支里 | 必须 End 配对 |
| Toggle / ToggleLeft | 开关;ToggleLeft 为勾选框在左 | |
| BeginToggleGroup / EndToggleGroup | 一个总开关控制整段子控件是否启用(灰显) | 必须 End 配对 |
滑动条
| API | 干什么用 |
|---|---|
| Slider / IntSlider | 在最小~最大间拖单个 float 或 int |
| MinMaxSlider | 同一范围内用两个滑块调左右界(ref 两个 float) |
提示与留白
| API | 干什么用 |
|---|---|
| HelpBox | 提示条;MessageType 为 None / Info / Warning / Error 控制图标与强调程度 |
| Space | 垂直方向空出指定像素 |
曲线与行列布局
| API | 干什么用 |
|---|---|
| CurveField | 编辑 AnimationCurve |
| BeginHorizontal / EndHorizontal | 子控件横向排列 |
| BeginVertical / EndVertical | 子控件纵向排列 |
| BeginScrollView / EndScrollView | 滚动视图;Vector2 存滚动偏移 |
EditorGUIUtility(辅助 API)
- 定位:EditorGUI 侧的实用工具类,补充资源加载、对象选择器、跨窗口事件、GUI/屏幕坐标互转、光标、色块与曲线预览等,不负责替代
EditorGUILayout画表单控件。
Editor Default Resources 与加载
| API | 干什么用 | 注意 |
|---|---|---|
| Load | 从 Assets/Editor Default Resources/ 按文件名+后缀加载 | 找不到返回 null |
| LoadRequired | 同上路径规则 | 找不到会直接报错 |
对象选择器与 Ping
| API / 机制 | 干什么用 |
|---|---|
| ShowObjectPicker | 弹出搜索选资源窗口;泛型为可选类型;参数含默认选中、allowSceneObjects、名称过滤字符串、controlID(示例常用 0) |
| GetObjectPickerObject | 读取对象选择器当前选中的 Object |
commandName + Event.current |
ObjectSelectorUpdated:选择变化时;ObjectSelectorClosed:选择器关闭时;在 OnGUI 里分支处理 |
| PingObject | 在 Project 等窗口定位并闪选传入对象(高亮选中) |
跨窗口命令事件与坐标
| 主题 | 要点 |
|---|---|
| CommandEvent + SendEvent | EditorGUIUtility.CommandEvent("命令名") 创建事件,对目标 EditorWindow.SendEvent(e);接收端:Event.current.type == ExecuteCommand 且 commandName 匹配 |
| 传递副作用 | 会自动打开接收方窗口,焦点移到该窗口 |
| 坐标系 | 屏幕:原点屏幕左上角;GUI:原点当前窗口左上角 |
| GUIToScreenPoint / GUIToScreenRect | GUI 空间 → 屏幕 |
| ScreenToGUIPoint / ScreenToGUIRect | 屏幕 → GUI 空间 |
| 与 BeginGroup 等组合 | 在布局/组内做转换时,要考虑布局偏移再换算 |
多显示器会参与坐标计算。
光标与装饰绘制
| API | 干什么用 |
|---|---|
| AddCursorRect | 在指定 Rect 内悬停时使用给定 MouseCursor(箭头、缩放、移动、手型等,见 MouseCursor) |
| DrawColorSwatch | 在矩形区域绘制纯色块;常和 ColorField 搭配作参考色 |
| DrawCurveSwatch | 在矩形内预览曲线;传入 AnimationCurve 与线色/背景色,可选 SerializedProperty |
Selection(编辑器当前选中)
- 用途:在编辑器脚本里读取当前在 Hierarchy / Project 等处的选中对象,不能用于运行时玩家包。
主选「一个」与多选「全部」
| 成员 | 含义 | 多选时 | 未选或类型不符 |
|---|---|---|---|
| activeObject | 当前主选 Object(场景对象、资源等) | 取第一个 | null |
| activeGameObject | 当前主选 GameObject | 第一个 | null(非 GameObject 选中也为 null) |
| activeTransform | 当前主选 Transform(对应 Hierarchy 场景对象) | 第一个 | null |
| objects | 当前选中 Object[] | 全部 | 未选可作 null;代码里常用 Selection.count 判断 |
| gameObjects | 当前选中 GameObject[] | 全部遍历 | 同 |
| transforms | 当前选中 Transform[] | 全部遍历 | 同 |
静态方法 · 委托
| API | 干什么用 |
|---|---|
| Contains | 判断某个 Object 是否在当前选中集合中(多选里包含也算) |
| GetFiltered(含泛型重载) | 在当前选中上按 SelectionMode 再筛选,得到 Object[] |
| selectionChanged | 选中变化时触发;在 OnEnable +=、OnDestroy -= 配对订阅 |
SelectionMode(可多选组合,用 | 位或)
| 模式 | 含义 |
|---|---|
| Unfiltered | 不过滤 |
| TopLevel | 只最上层,不含子对象 |
| Deep | 父与子都包含 |
| ExcludePrefab | 排除预制体 |
| Editable | 只要可编辑对象 |
| OnlyUserModifiable | 仅用户可修改内容 |
| Assets | 只返回 Assets 下资源 |
| DeepAssets | Assets 含子文件夹深度 |
Event(编辑器内输入)
- 用途:在编辑模式下读键盘、鼠标等输入;运行时监听用 Input,编辑器里用 Event。
- 使用位置:OnGUI、SceneView 相关回调等。
- 入口:
Event.current取当前正在处理的事件。
常用字段
| 类别 | 成员 | 含义 |
|---|---|---|
| 修饰键 | alt、shift、control、command | 是否按住 Alt / Shift / Ctrl / Win 或 Mac Command |
| 鼠标 | isMouse、button、mousePosition | 是否鼠标事件;按键索引;GUI 空间鼠标位置 |
| 键盘 | isKey、character、keyCode | 是否键盘事件;字符;按键码 |
| 事件类型 | type(配合 EventType) | 区分按下、抬起、拖拽等(与鼠标/键盘阶段配合) |
| 其它 | capsLock、delta | 大写锁定;鼠标位移增量 |
| 命令串 | commandName | 如 Copy / Paste / Cut 等(与菜单命令对应) |
| 其它 | functionKey、numeric | 功能键;数字键盘区状态 |
处理完自定义逻辑后可调用 Use(),阻止事件继续传递,减少与编辑器其它逻辑冲突。
Inspector 自定义(Editor · SerializedObject)
角色
| 概念 | 作用 |
|---|---|
| SerializedObject | 表示正在被检视的组件/对象的序列化视图 |
| SerializedProperty | 表示其上某一个字段(或子字段) |
| Editor.target | 当前正在编辑的组件实例 |
基本流程
- 新建类继承 Editor,加 **
[CustomEditor(typeof(你的 MonoBehaviour))]**;脚本放 Editor 程序集。 - OnEnable 里
serializedObject.FindProperty("字段名")缓存 SerializedProperty。 - 重写 OnInspectorGUI:整体包在
serializedObject.Update()与ApplyModifiedProperties()之间再画控件、改 SerializedProperty。
数组 / List
| 方式 | 要点 |
|---|---|
| PropertyField | 按类型自动画数组或 List;不要再塞进 BeginFoldoutHeaderGroup 与自带折叠冲突,否则会报错 |
| 手写 | 用 arraySize、InsertArrayElementAtIndex、DeleteArrayElementAtIndex、GetArrayElementAtIndex 控制容量与每一项 |
可序列化自定义类
- 类型加 **
[Serializable]**;字段用 SerializedProperty 绑定。 - 子字段:
FindPropertyRelative("子名")或 **FindProperty("父.子")**,二选一。
Dictionary 与 Inspector
- Unity 不直接序列化 Dictionary 到 Inspector;用两个 List(或数组)存键值,内存里再维护 Dictionary。
- ISerializationCallbackReceiver:OnBeforeSerialize 里把字典写入两列表;OnAfterDeserialize 里从列表还原字典。
- 自定义 Inspector 里对 keys / values 两个 SerializedProperty 做增删改,与数组/List 手写逻辑类似。
Scene 视图 · Handles
- 定位:在 Scene 窗口绘制 3D 辅助线、面、手柄等;与 GUI / EditorGUI 类似,但面向场景视图。
- CustomEditor 路线:前两步与自定义 Inspector 相同(继承 Editor、
[CustomEditor(typeof(T))]),再实现void OnSceneGUI();仅当选中挂载该组件的对象时执行。 - EditorWindow 路线:**
SceneView.duringSceneGui +=** 在窗口 OnEnable 订阅,OnDestroy 里 -= 移除。
颜色 · 文字 · 线
| API | 干什么用 | 注意 |
|---|---|---|
| Handles.color | 设置后续线框等绘制颜色 | Label 不受此颜色控制,需 GUIStyle |
| Label | 在世界坐标处显示文字 | |
| DrawLine | 实线段 | 含线宽重载 |
| DrawDottedLine | 虚线段 |
弧 · 圆 · 体
| API | 干什么用 |
|---|---|
| DrawWireArc / DrawSolidArc | 圆弧:圆心、法线、起始朝向、角度、半径 |
| DrawSolidDisc / DrawWireDisc | 填充圆 / 线框圆 |
| DrawWireCube | 线框立方体(中心、尺寸) |
| DrawAAConvexPolygon | 按顶点画凸多边形 |
标准移动 / 旋转 / 缩放手柄
| API | 干什么用 | 易错点 |
|---|---|---|
| DoPositionHandle / PositionHandle | 位置手柄 | 传入 Vector3.zero、Quaternion.identity 等写死值时,手柄不跟物体动 |
| DoRotationHandle / RotationHandle | 旋转手柄 | |
| DoScaleHandle / ScaleHandle | 缩放手柄 | 最后一参为 HandleUtility.GetHandleSize(参考点);GetHandleSize(zero) 与传物体位置时柄长表现不同 |
自由移动 / 旋转
| API | 干什么用 |
|---|---|
| FreeMoveHandle | 无轴约束移动;含步进(Ctrl)、HandleCap(RectangleHandleCap、CircleHandleCap、ArrowHandleCap 等) |
| FreeRotateHandle | 自由旋转 |
在 Scene 里叠 IMGUI
| API | 干什么用 |
|---|---|
| BeginGUI / EndGUI | 中间写 GUI / GUILayout |
| SceneView.currentDrawingSceneView | 当前绘制的 SceneView;position 取窗口宽高,用于 GUILayout.BeginArea 等布局 |
HandleUtility(句柄与 Scene 交互)
- 定位:处理 Handles 相关的鼠标、坐标转换、拾取等;鼠标位置用 **
Event.current.mousePosition**(回顾)。
| API | 干什么用 |
|---|---|
| GetHandleSize | 给定世界坐标处句柄应有的大致尺寸,随距离与相机缩放变化 |
| WorldToGUIPoint | 世界坐标 → Scene 视图 GUI 像素,便于控件贴着物体画 |
| GUIPointToWorldRay | 屏幕点 → 世界射线,用于 Raycast |
| DistanceToLine | 鼠标到空间线段的最短距离,可做悬停判断 |
| PickGameObject | 按屏幕坐标拾取场景 GameObject |
Gizmos(MonoBehaviour 上的 Scene 辅助绘制)
- 与 Handles 分工:Gizmos 侧重辅助线、图标、几何示意;Handles 侧重可交互编辑手柄。
- 挂载位置:写在继承 MonoBehaviour 的脚本里(非 Editor 脚本)。
| 回调 | 何时调用 |
|---|---|
| OnDrawGizmos | 每帧绘制,Scene 里始终可见 |
| OnDrawGizmosSelected | 仅当该 GameObject 被选中时每帧绘制 |
颜色 · 立方体 · 视锥 · 矩阵
| 要点 | 说明 |
|---|---|
| Gizmos.color | 设置后续绘制颜色 |
| DrawCube / DrawWireCube | 实心 / 线框立方体 |
| DrawFrustum | 视锥:中心、FOV、远/近裁切面、宽高比 |
| Gizmos.matrix | 默认立方体/视锥不随物体旋转;用 Matrix4x4.TRS 或 localToWorldMatrix 让绘制跟随物体;Matrix4x4.identity 还原 |
贴图与图标
| API | 干什么用 | 注意 |
|---|---|---|
| DrawGUITexture | 在 Rect 上画贴图 | 仅 xy 跟物体走,z 无效;显示可能「默认反过来」 |
| DrawIcon | 世界坐标处画内置图标 | 图片放在 Assets/Gizmos/,参数为文件名 |
线 · 网格 · 射线 · 球
| API | 干什么用 |
|---|---|
| DrawLine | 线段 |
| DrawMesh / DrawWireMesh | 实心网格 / 线框网格 |
| DrawRay | 从点沿方向射线 |
| DrawSphere / DrawWireSphere | 实心球 / 线框球 |
EditorUtility(编辑器通用工具)
- 定位:Unity 编辑器里的实用工具类,各类拓展里都能调;只存在于编辑器,不能进玩家包。
模态对话框与进度
| API | 干什么用 | 注意 |
|---|---|---|
| DisplayDialog | 单确认类弹窗 | 阻塞,关掉前后面逻辑不继续 |
| DisplayDialogComplex | 三键面板 | 返回 0 / 1 / 2 对应三个按钮;参数顺序为「左、右、中」式三键名(以 API 为准) |
| DisplayProgressBar | 显示进度 0~1 | 不阻塞逻辑,但要记得 ClearProgressBar |
系统文件 / 文件夹对话框
| API | 干什么用 |
|---|---|
| SaveFilePanel | 系统「另存为」,得完整路径,可写任意盘 |
| SaveFilePanelInProject | 限制在工程目录内保存 |
| SaveFolderPanel | 选保存用文件夹 |
| OpenFilePanel | 打开单个文件 |
| OpenFolderPanel | 打开文件夹 |
取消或关闭时常返回空字符串(可用 != "" 判断)。
其它常用
| API | 干什么用 |
|---|---|
| CompressTexture | 将 Texture2D 压成指定 TextureFormat 与质量;常配合导入管线 |
| CollectDependencies | 传入根 Object[],返回其依赖资源列表;可赋给 Selection.objects 做全选依赖 |
AssetDatabase(编辑器侧资源数据库)
- 定位:编辑器里对 Project / Assets 做增删改查的静态 API;仅编辑器,不进包。
- 路径:一律从
Assets/...起;涉及文件的 API 通常要写扩展名。
| API | 干什么用 | 注意 |
|---|---|---|
| CreateAsset | 把内存里的资源对象写成磁盘资产 | 不能在 StreamingAssets;不能直接创建 Prefab;路径带后缀;典型如材质球 |
| CreateFolder | 在父路径下建新文件夹 | 参数为 (父文件夹, 新文件夹名) |
| CopyAsset / MoveAsset | 拷贝 / 移动资产 | 目标路径带后缀 |
| DeleteAsset | 删单个资产 | — |
| DeleteAssets | 批量删 | 失败路径写入传入的 List<string> |
| GetAssetPath | Object → 工程内路径 |
常配合 Selection |
LoadAssetAtPath<T> |
按路径加载单个主资产 | 编辑器工具用;运行时打包流程不同 |
| LoadAllAssetsAtPath | 同路径下全部子对象 | 图集等:返回 **Object[]**,首个常为图集本体,其后为子 Sprite |
| Refresh | 刷新资源数据库与 Project 视图 | 用 File 等非 Unity API 写进 Assets 后常需调用,否则窗口不更新 |
| GetImplicitAssetBundleName | 查路径对应的 AB 包名 | 路径从 Assets/ 起 |
PrefabUtility(编辑器侧 Prefab 操作)
- 定位:创建 / 加载 / 保存 / 实例化 Prefab 资产;仅编辑器。
| API | 干什么用 | 注意 |
|---|---|---|
| SaveAsPrefabAsset | 把场景里的 GameObject 存成 .prefab |
可先 new GameObject 再保存;不想留在场景里可 DestroyImmediate |
| LoadPrefabContents | 把 Prefab 展开到可编辑的临时实例(隐形场景) | 用于加组件、改层级等;与 Unload 成对 |
| UnloadPrefabContents | 释放 LoadPrefabContents 打开的实例 | 不配会泄漏/状态异常 |
| SavePrefabAsset | 把已加载的 Prefab 资产写回磁盘 | 常配合 LoadAssetAtPath<GameObject>;存的是资产不是场景实例 |
| InstantiatePrefab | 在编辑器里把 Prefab 摆进当前场景 | 与 LoadPrefabContents 不同:后者为编辑资产内容 |
EditorApplication(编辑器本体状态与回调)
- 定位:监听编辑器级更新与工程变化、查询播放/暂停/编译/刷新、取 Unity 安装路径、控制进入/退出播放模式等。
| 能力 | 说明 |
|---|---|
| 静态事件 | update(每帧);hierarchyChanged;projectChanged;playModeStateChanged;pauseStateChanged |
| 状态 | isPlaying / isPaused / isCompiling / isUpdating(刷新 AssetDatabase 时) |
| 路径 | applicationContentsPath(安装目录 Data);applicationPath(Unity.exe) |
| 常用调用 | EnterPlaymode / ExitPlaymode;Exit 退出编辑器等(Lesson 注释) |
订阅 update 等事件须在 OnEnable/OnDestroy 里 += / -= 成对注销。
CompilationPipeline(UnityEditor.Compilation)
- 用途:感知脚本编译进度;典型场景是动态生成代码后要等整次编译结束再使用新类型。
| 回调 | 何时触发 | 参数要点 |
|---|---|---|
| assemblyCompilationFinished | 某一个程序集编译完 | (程序集名, CompilerMessage[]) — 含警告/错误 |
| compilationFinished | 全部程序集编译完 | object(与活动生成状态相关) |
在 OnEnable/OnDestroy 中 += / -= 订阅与取消。
AssetImporter 与 AssetPostprocessor(导入管线)
- 分工:AssetPostprocessor 写导入过程的通用逻辑;AssetImporter(及子类)对应 Inspector 里各类型导入设置。
- AssetPostprocessor:常用 **
assetImporter(转为具体 Importer)、assetPath**;按资源类型实现 OnPreprocess* / **OnPostprocess***(纹理 / 模型 / 音频等)。 - 典型用途:一类资源统一默认导入参数;导入后批量后处理(如 OnPostprocessTexture 里调用 EditorUtility.CompressTexture)。
- 按需过滤:只想处理部分文件时,可用路径/命名规则收窄。
- AssetImporter 子类(常见):TextureImporter、ModelImporter、AudioImporter、VideoClipImporter、ScriptedImporter(自定义扩展名导入)。
49.3 面试题精选
基础题
1. Editor 程序集、UnityEditor 与 MenuItem / AddComponentMenu
题目
为什么大量使用 UnityEditor 的脚本要放在 Editor 文件夹(或独立 Editor 程序集)?MenuItem 与 AddComponentMenu 分别挂在什么代码上、各自影响什么界面?
深入解析
- UnityEditor 只在编辑器内存在,运行时代码不能引用;打进包体会失败或报错。
Editor文件夹将脚本编译进 Editor-only 程序集,与玩家代码隔离。 - MenuItem:修饰 static 方法,扩展顶部菜单 / GameObject / Assets / 组件右键等;必须能引用
UnityEditor。 - AddComponentMenu:在 UnityEngine 命名空间,给继承 MonoBehaviour 的类出现在 Add Component 里的分类路径;组件仍属运行时脚本,一般不放进
Editor。
答题示例
UnityEditor 不能进玩家包,编辑器工具代码放 Editor 程序集。MenuItem 是加编辑器菜单命令的,方法 static,写在 Editor。AddComponentMenu 只是 Component 菜单里的显示路径,脚本还是挂在物体上的 MonoBehaviour,跟普通游戏脚本放一起。
参考文章
- 2.自定义菜单栏拓展
2. EditorGUI 与 EditorGUILayout、典型使用场景
题目
编辑器 IMGUI 里 EditorGUI 和 EditorGUILayout 的主要差别是什么?拓展里一般在什么地方调用它们?
深入解析
- EditorGUI:按 Rect 手动布局,与 GUI 对应,适合已知矩形或要精细贴齐的区域。
- EditorGUILayout:自动布局,与 GUILayout 对应,多数工具窗、Inspector 拓展用 EditorGUILayout 写控件更省事。
- 常见调用位置:EditorWindow.OnGUI、自定义 Editor.OnInspectorGUI 等;可与 GUI / GUILayout 混用。
答题示例
EditorGUI 要自己算 Rect 摆位置;EditorGUILayout 自动排版。做编辑器窗口和自定义 Inspector 一般用 EditorGUILayout 多。都在 OnGUI 或 OnInspectorGUI 里画。
参考文章
- 4.EditorGUI-EditorGUI是什么、系列 EditorGUILayout 篇
3. Gizmos 与 Handles 的分工
题目
Gizmos 和 Handles 分别解决什么问题?脚本通常写在哪里、和 Scene 里「能不能交互」有什么关系?
深入解析
- Gizmos:辅助显示(线框、图标、范围等),多在 MonoBehaviour 的
OnDrawGizmos/OnDrawGizmosSelected中绘制,非 Editor 脚本,偏只读示意。 - Handles:可编辑手柄(移动/旋转/缩放等),在 Editor 侧 OnSceneGUI 或 SceneView.duringSceneGui 等里绘制,常与工具逻辑、Undo 配合。
- 面试常追问:选中才画用
OnDrawGizmosSelected;要场景内拖拽编辑走 Handles。
答题示例
Gizmos 是画给策划程序看的辅助线、范围,写在普通 MonoBehaviour 上,一般不交互。Handles 是 Scene 里能拖的手柄,在 Editor 的 OnSceneGUI 里画。一个偏展示,一个偏编辑。
参考文章
- 34~38 Gizmos 篇、27~33 Handles 篇
进阶题
1. GetWindow 与 CreateWindow
题目
EditorWindow.GetWindow 与 CreateWindow 在实例数量上典型差异是什么?什么需求下必须用 CreateWindow?
深入解析
- GetWindow:同类型窗口常表现为单例,再次打开往往复用已有实例,适合全局工具、设置面板。
- CreateWindow:允许同类型多实例,适合多文档、多配置对照、并排多个独立面板。
- 工程上:需要「同类工具开多个」时用 CreateWindow,避免 GetWindow 抢同一个实例。
答题示例
GetWindow 同类型通常就一个窗口。CreateWindow 可以同类型开多个。要并排开多个同类编辑器窗口时用 CreateWindow。
参考文章
- 3.自定义窗口拓展
2. AssetDatabase 的路径、Refresh 与 LoadAssetAtPath
题目
AssetDatabase 里资源路径通常怎么写?什么情况下必须调用 Refresh?LoadAssetAtPath 和运行时 Resources.Load 等有什么边界?
深入解析
- 路径一般以
Assets/...为根,带扩展名;创建/拷贝/移动等多处要求写全路径。 - Refresh:用 System.IO 等绕过 Unity API 直接改 Assets 下文件时,Project 窗口与数据库可能不更新,需 **AssetDatabase.Refresh()**。
- LoadAssetAtPath:仅编辑器下按工程路径加载资产;运行时包内加载走 Resources、Addressables、AssetBundle 等,不把 AssetDatabase 打进包。
答题示例
路径从 Assets 写起,要带后缀。用 File 写进 Assets 了要 Refresh 一下才能在 Project 里看到。LoadAssetAtPath 是编辑器工具用的,真机跑游戏不能用这套,运行时有自己的加载方式。
参考文章
- 43~44 AssetDatabase 篇
3. PrefabUtility:编辑资产内容 vs 摆进场景
题目
LoadPrefabContents + UnloadPrefabContents、InstantiatePrefab、SavePrefabAsset 分别典型用于什么?Load 与 Instantiate 容易混在什么地方?
深入解析
- LoadPrefabContents:把 Prefab 展开成可编辑根物体(隐形场景),改组件、层级后常再 SaveAsPrefabAsset;必须与 UnloadPrefabContents 配对。
- InstantiatePrefab:在当前编辑场景里生成预制实例,偏「摆关卡」。
- SavePrefabAsset:把已加载的 Prefab 资产(如
LoadAssetAtPath得到的)的修改写回磁盘;与对场景实例改完点 Apply 的流程不同。 - 易混:LoadPrefabContents 不是「往场景里放一个可玩的实例」那种 Instantiate,而是改磁盘上的 Prefab 资产的工作流。
答题示例
LoadPrefabContents 是把 prefab 拉出来编辑,改完保存再 Unload,两个要配对。InstantiatePrefab 是编辑器里往场景里实例化。SavePrefabAsset 是保存对 prefab 资产本身的修改。别把 LoadPrefabContents 当成普通 Instantiate。
参考文章
- 45.PrefabUtility
深度题
1. EditorWindow:OnGUI、Update、OnInspectorUpdate
题目
EditorWindow 里 OnGUI、Update、OnInspectorUpdate 调用节奏有何不同?各适合放什么逻辑?
深入解析
- OnGUI:每帧绘制 IMGUI,按钮、布局、控件交互以这里为主。
- Update:窗口每帧 tick,适合不依赖 GUI 绘制顺序的持续逻辑(计时、轮询状态等)。
- OnInspectorUpdate:随 Inspector 刷新触发,适合与当前 Selection / 检视对象同步的轻量更新,避免比 OnGUI 更频繁的无效重绘。
- 取舍:纯界面跟 OnGUI;跟 Inspector 选中对象对齐用 OnInspectorUpdate。
答题示例
OnGUI 每帧画界面。Update 也是每帧,放不画图但要每帧跑的逻辑。OnInspectorUpdate 跟着 Inspector 刷新,适合跟选中对象同步。要跟检视器对齐就用 OnInspectorUpdate。
参考文章
- 3.自定义窗口拓展
2. 自定义 Inspector:SerializedObject 与直接改 target
题目
自定义 Editor 里为什么推荐 serializedObject.Update / ApplyModifiedProperties 配合 SerializedProperty,而不是在 OnInspectorGUI 里直接改 target 上的字段?
深入解析
- SerializedObject 走 Unity 序列化与 Undo 系统,ApplyModifiedProperties 能正确标记脏、支持撤销、与多对象编辑更一致。
- 直接改 target 容易绕过 Undo、Inspector 刷新异常,且 多选同一组件 时行为难保证。
- 标准骨架:
Update()→ 画控件并改 SerializedProperty →ApplyModifiedProperties()。
答题示例
用 SerializedObject 和 SerializedProperty 改,能接 Undo,和多选、脏标记都一致。直接改 target 容易不能撤销,和多对象编辑也容易出问题。一般是 Update,改属性,再 Apply。
参考文章
- 23~26 Inspector 窗口拓展篇
3. 导入管线:AssetPostprocessor 与 AssetImporter 子类
题目
AssetPostprocessor 在资源导入流程里起什么作用?OnPreprocess* 与 OnPostprocess* 典型各做什么?和 TextureImporter 等 AssetImporter 子类是什么关系?
深入解析
- AssetPostprocessor 提供导入各阶段的全局回调,可据 assetPath 做命名规则过滤,实现「某类目录下纹理统一设为 Sprite」等批量策略。
- OnPreprocess*:导入完成前改 Importer 设置(如纹理类型、压缩)。
- OnPostprocess*:导入完成后对 Object 再加工(如 OnPostprocessTexture 里再压格式)。
- AssetImporter 子类是 Inspector 里该类型资源的设置模型;回调里通常
assetImporter as TextureImporter等去改。
答题示例
AssetPostprocessor 是导入流水线里的钩子,可以统一改导入设置或导入后处理。Pre 是在导入完成前改 Importer,Post 是导入后再动资源。TextureImporter 那些是具体资源在 Inspector 里的设置类型,在回调里转型改就行。
参考文章
- 48.AssetImporter和AssetPostprocessor
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com