关于最近Unity编辑器代码使用总结
Unity编辑器扩展的代码总结
前言
最近我写了些Unity相关的工具,比如打图集、设定多语音等问题。我想着趁我现在还记得自己在扩展中遇到的问题,我就写一篇文章来记录一下。
环境
Window10系统
Unity2022.3.8f1c1
EditorWindow
Unity中其实有很多方法可以生成一个编辑器窗口,然后我们就按照这些方法的规则去实现我们想要的编辑器扩展。但这其中我觉得继承EditorWindow去实现是很好用的一个方法。EditorWindow官方文档中是这样描述的:从此类派生以创建编辑器窗口。创建自己的自定义编辑器窗口,这些窗口可以自由浮动,也可以作为选项卡停靠,就像 Unity 界面中的原生窗口一样。而且其本身的使用并不困难,在官方的文档中也存在一个使用示例。
在实现我们想法的过程中,我们会经常用到如EditorGUILayout,EditorGUI,EditorGUIUtility,GUI,GUILayout,GUIUtility,在OnGUI函数中。
关于布局
在UI布局方面,EditorWindow默认采用垂直布局。当然我们也可以使用代码来更改布局。比如下面的代码:
1 | private void OnGUI() |
其结果如下:

上面我们通过BeginHorizontal来决定后面的UI布局是水平布局,然后再调用EndHorizontal来结束水平布局。如果我们还想在水平布局中加入垂直布局,那么我们可以调用BeginVertical开始垂直布局,然后再使用EndVertical来结束。(这里我只给出了EditorGUILayout的链接,但是其实GUILayout也有这样的功能。)
窗口本身的修改
在官方的示例中,打开窗口的代码如下:
1 | [ ] |
但其实我们不调用Show函数也是可以打开窗口的。对于打开的窗口,我们可以使用一些API来修改窗口的标题和大小。比如:titleContent就可以修改窗口的标题,maxSize可以定义窗口最大的大小,等等。具体大家可以查看一下EditorWindow,文档中都有对应的说明。
一些简单的UI
Unity本身已经帮我们封装了一些UI,我们可以通过这些UI来实现一些功能。比如上面的代码中标签:LabelField;还有我们经常用到的文本输入:TextArea,等等。大多数GUILayout可以实现的,EditorGUILayout也可以实现。除了普通按钮以外。对于普通按钮,我们仍要使用GUILayout.Button去实现一个普通按钮。
很多实现UI的API中,最后都会带上一个GUILayoutOption[] options这样的参数。当然我们可以不传任何的信息。但是如果你觉得默认效果不好看,那么你就要使用这个来做处理。在GUILayoutOption官方文档中,它也有说明传入怎样的参数。大家看一下文档就好了。
显示我们的变量
我们在窗口中建立一个输入,本质上我们是想获取到这些输入然后传到我们的变量中。而下面的代码便可以实现我们这样的要求:
1 | private string val; |
我设定了一个string类型名为val的变量,然后我创建了一个文本来获取到用户的输入。如果仅仅是string类型自然简单,但是如果我们要的是int类型呢?那我们不是还要针对用户的输入进行判断来预防一些非数字的输入。针对这样的情况,Unity封装了一些API来帮助我们实现值获取。
我们可以使用IntField,DoubleField,Vector2Field等来帮助我们实现我们想要的变量输入(更多信息请移步EditorGUILayout官方文档)。虽然Unity提供了很多的封装,但是它不能完全满足我们个人的需求。比如你不能找到任何一个API调用后直接就帮我们弄好了一个List类型的输入。如果我自定义了一个类,那么Unity又怎么会特别提供一个API去显现我自定义的类呢?
自创类的展示方法
其实Unity还提供了另外一种显示变量的方法,来满足那些由我们自定义出来了类。这个方法本身有一个限制,那就是类型本身可支持序列化。简单点来说就是当我们写脚本时,那些添加[SerializeField]后可以在Inspector窗口中显示的那些类型才可以支持(所以Dictionary类型是不受支持的)。虽然其本身仍然有所限制,但是他已经能完成我们打部分的需求了。具体代码如下:
1 | using System; |
运行结果如下:

SerializedObject和SerializedProperty需要只初始化一次。所以我这边放在了OnEnable这个特殊的函数中,你可以理解它的作用和脚本生命周期里的OnEnable函数一致。我们要先初始化SerializedObject,然后再使用SerializedObject.FindProperty让SerializedProperty和我们的定义的变量对应上。
在OnGUI函数中,我们要先使用SerializedObject.Update去更新序列化对象,而在末尾时要使用SerializedObject.ApplyModifiedProperties让我们的输入得以传给变量。而这些变量的显示就要使用EditorGUILayout.PropertyField。
变量过多导致UI占用窗口过长的解决方案
我本来认为这样实现的效果和脚本在Inspector窗口里的差不多。特别是像List这样,一个变量会有很多的子变量。当其超过某个数量的时候就会出现滚轮。但是在编辑器扩展中,类似List这样的变量它只会不断的扩大。这样导致了你想操作某个List元素时,你只能扩大窗口然后得到后面的元素。可是窗口的大小一般不足以展示超过100个的变量。我在网上找了很久都没有解决的方法。所以我自己就想到了下面这样的操作:
1 | private Vector2 pos; |
我们可以使用EditorGUI.GetPropertyHeight来获取到属性区域的高度。当这个高度超过某个值时,我们就使用一个滚轮区将其包围起来。这里所用到的API为EditorGUILayout.BeginScrollView和EditorGUILayout.EndScrollView。因为我只想这个属性高度占据200,所以我给滚轮区设定的最大高度也是200。不然滚轮区会一致延伸到窗口底部。
自定义变量显示的效果
在项目开发中,我就遇到一个效果是使用Unity自身的API完不成的。所以这就需要我们自己去做这种变量的UI显示。其核心就是PropertyDrawer这个类。这里我找到一个博主写的文章,大家直接看他的文章就好了。文章链接。文章中出现的Unity的API所对应的相关文档PropertyAttribute,PropertyDrawer,CustomPropertyDrawer。这个文章是教如何自定义变量的名称显示。但是其本身也可以改为自定义类型显示,就像是这个PropertyDrawer文档中显示描述的那样。
为了方便我自己查阅,这里我将代码贴上。核心代码如下:
1 | using UnityEngine; |
1 | using UnityEditor; |
使用:
1 | private class Data |