Unity编辑器拓展(一|属性/字段)
Unity编辑器针对字段的拓展一般使用特性(Attribute)。这些特性需要直接写在字段声明位置前面来修饰这个字段,影响其在Inspector的显示。这些特性可能与其修饰的字段相关,规范其取值或显示形式;也可能与这些字段没有任何关系,仅仅是依附于它。
Unity内置了很多这类常用特性来供我们使用
- [Range(num1,num2)]:限定数值取值范围为[num1,num2]
- [Multiline(num)]:提供num行的输入框
- [TextArea(num1,num2)]:提供一个最小num1行,最大num2行的文本框
- [SerializeField]:序列化字段
- [NonSerialized]:反序列化字段,并将其在Inspector隐藏
- [HideInInspector]:将一个字段在Inspector隐藏
- [FormerlySerializedAs(“str”)]:当变量名发生改变时,可以保存原来str的值
- [Header(“str”)]:添加名为str的标题
- [Space(num)]:添加大小为num的间隔
- [Tooltip(“str”)]:添加信息为str的提示(鼠标悬浮在字段名上显示)
- [ColorUsage(true)]:设定取色面板类型(是否可修改alpha、是否是HDR以及取值范围)
下面的内容将讲解如何编写自己的特性。
构成
这类拓展至少需要两个脚本,分别是下面两者的子类:
- PropertyAttribute
- GUIDrawer
前者的功能是定义特性的数据结构,获取绘制GUI所需的所有参数。它只储存参数,不包含任何函数。例如[Tooltip(“str”)]实际就调用了TooltipAttribute的构造函数,传入了”str”这一个参数。
后者的功能是利用特性的参数(以及特性修饰的字段)来绘制GUI
PropertyAttribute
PropertyAttribute在UnityEngine命名空间下,是Atrribute的直接子类。对于继承它的特性,一般来说其中所有字段都用public和readonly修饰,通过构造函数赋值。
注意,它的子类不能放在Editor文件夹中
另外按照规范,特性的名称应该以大写字母开头,并以 “Attribute” 结尾,例如 “SeperatorAttribute”。C#编译器也会自动识别这一命名方式,允许我们直接用“Seperator”这个名字来使用特性。
下面我们实现一个叫做Seperator的特性,用来在Inspector中绘制一条分割线。
1 | using UnityEngine; |
可见未来我们要使用Seperator特性时,应该写[Seperator(height,spacing)]或者[Seperator](height和spacing为构造函数默认的1和10)
GUIDrawer
GUIDrawer有两个子类,需要根据特性功能是否需要用到被修饰的字段来决定继承哪一个
继承GUIDrawer的类可以且最好放进Editor文件夹
DecoratorDrawer
不影响字段的绘制,而是纯粹基于从其对应的PropertyAttribute中获取的数据来绘制装饰元素。如Header特性只是依附于一个字段来绘制标题,至于其所依附的字段如何是完全不用关心的;前面我们准备编写的的Seperator特性也属于这一类。
(Attribute纯粹储存数据,DecoratorDrawer应用数据绘制。通过atrribute字段获取数据)
变量attribute即为Drawer对应的特性,使用时我们需要将其强转为相应的类型。
下面就是实现Seperator特性需要的第二个脚本1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21using UnityEngine;
using UnityEditor;
[//将Drawer与Attribute绑定 ]
public class SeperatorDrawer : DecoratorDrawer
{
//GUI的绘制逻辑在OnGUI编写;
public override void OnGUI(Rect position)
{
SeperatorAttribute sa = attribute as SeperatorAttribute;
//position为常规该行元素应该占据的位置,我们在这里相对于position绘制一条高度为sa.Height,与上一行间距为sa.Spacing的横线
Rect r = new Rect(position.xMin, position.yMin + sa.Spacing, position.width, sa.Height);
EditorGUI.DrawRect(r, new Color(88f / 255, 88f / 255, 88f / 255));
}
//GetHeight决定编辑器应当为特性的绘图预留多高的空间
public override float GetHeight()
{
SeperatorAttribute sa = attribute as SeperatorAttribute;
return sa.Height + sa.Spacing * 2;
}
}
实现效果如下:
OnGUI的position参数其实还是值得讲一下的,这里没看懂的话可能会折磨很久…补一张图
这里的坐标系使用的是第四象限,(position.xMin,position.yMin)为元素左上角的坐标,position.width和position.height则描述的是常规一行的尺寸。
PropertyDrawer
这类特性影响或需要使用字段。比如Range特性用一个滑动条来限制数值类型字段的取值范围,那我们就需要让这个特性能够覆盖原字段的输入框,在相应位置绘制滑动条,且允许使用者通过与滑动条交互来给字段赋值。DecoratorDrawer无法获得其修饰字段的引用,显然做不到这件事。
下面是一个应用在Vector2类字段的特性,能够用Slider规范字段x和y的取值范围,且保证x<=y1
2
3
4
5
6
7
8
9
10
11
12
13using UnityEngine;
[ ]
public class MinMaxRangeAttribute : PropertyAttribute
{
public readonly float min;
public readonly float max;
public MinMaxRangeAttribute(float min, float max)
{
this.min = min;
this.max = max;
}
}
根据我们使用Seperator的经验,我们应该用[MinMaxRange(min,max)]来使用这个特性
1 | using UnityEngine; |
注意!:PropertyDrawer不可以使用EditorGUILayout,必须用EditorGUI。使用EditorGUILayout将会报错
效果展示:
延伸
Unity还有很多应用在类或方法上的特性,可以给我们的开发带来很大的便利。
Unity手册—Attribute汇总说明