通常来讲,当咱们要扩展编辑器时,咱们会从Editor类继承,为本身的MonoBehaviour实现不一样的外观。
可是若是有一个struct/class,在许多地方被使用,Unity默认的外观又不够好看,此时想修改它的外观,就须要使用PropertyDrawer了。html
上图是一个Monobehaviour中包含一个简单的struct(TileCoord类),包含两个int,可是显示效果十分别扭。c#
实现对应的PropertyDrawer后编辑器
相对于Editor类能够修改MonoBehaviour的外观,咱们能够简单的理解PropertyDrawer为修改struct/class的外观的Editor类。
实现上面的效果的代码以下ide
[CustomPropertyDrawer(typeof(TileCoord))] public class TileCoordEditor : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { var x = property.FindPropertyRelative("x"); var y = property.FindPropertyRelative("y"); float LabelWidth = EditorGUIUtility.labelWidth; var labelRect = new Rect(position.x, position.y, LabelWidth, position.height); var xRect = new Rect(position.x + LabelWidth, position.y, (position.width - LabelWidth) / 2 - 20, position.height); var yRect = new Rect(position.x + LabelWidth + (position.width - LabelWidth) / 2 - 20 , position.y, (position.width - LabelWidth) / 2 - 20, position.height); EditorGUIUtility.labelWidth = 12.0f; EditorGUI.LabelField(labelRect, label); EditorGUI.PropertyField(xRect, x); EditorGUI.PropertyField(yRect, y); EditorGUIUtility.labelWidth = LabelWidth; } //须要自定义高度 // public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { // return // } }
能够看到跟继承Editor的操做很类似。不过额外在OnGUI提供了三个参数,依次解释一下:
position:该属性在Editor中被分配到的位置、大小。注意这里的x,y对应的是左上角,跟游戏中的左下角不一样(由于Inspector是从上到下绘制)。大小的宽度由Inspector的宽度决定,而高度须要经过在类中override一个方法来自定义高度,不然默认为一行高。ui
property:待绘制的属性自己。Unity在编辑器的API中大部分的实际的值都是用一个SerializedProperty表示的,实际上就是对值的一个包装。经过这个包装,当咱们修改值的时候,Unity能够知道此次操做,相似刷新界面、Undo、prefab修改之类的信息均可以帮咱们处理好。坏处在于咱们得经过相似FindPropertyRelative的方法,用字符串去寻找内部的值(SerializedProperty是个嵌套结构,内部的数据也是SerializedProperty)。在Unity升级C#来支持nameof以前,咱们只能尽可能避免修改字段的名字了。同时,咱们绘制这些property的时候能够直接用EditorGUI.PropertyField(property),而不用相似的 x = EditorGUI.IntField(x)这样的调用。设计
label:这个值在MonoBehaviour里的字段名。3d
另外,在PropertyDrawer中不能使用带Layout的类,即EditorGUILayout、GUILayout。( http://answers.unity3d.com/questions/661360/finally-a-solution-cant-use-guilayout-stuff-in-pro.html )用了的话会报个迷之错误。不过彷佛并非bug,而是设计如此(不容许PropertyDrawer用Layout)。code
最后说一下EditorGUIUtility.labelWidth的使用。htm
我在最开始实现这个PropertyDrawer时,代码以下blog
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { var x = property.FindPropertyRelative("x"); var y = property.FindPropertyRelative("y"); float LabelWidth = 50; var labelRect = new Rect(position.x, position.y, LabelWidth, position.height); var xRect = new Rect(position.x + LabelWidth, position.y, (position.width - LabelWidth) / 2 , position.height); var yRect = new Rect(position.x + LabelWidth + (position.width - LabelWidth) / 2 , position.y, (position.width - LabelWidth) / 2 , position.height); EditorGUI.LabelField(labelRect, label); EditorGUI.PropertyField(xRect, x); EditorGUI.PropertyField(yRect, y); }
最后效果以下
能够看到,int的输入框被两个label挤到了右边。而我想要的效果是相似Box Collider2D里的那种样式。
而咱们用PropertyField绘制的时候,并无设置Label宽度的办法。
随后找到资料,发现EditorGUIUtility.labelWidth这个属性。表明的是Label的宽度。比较奇葩的是它是一个可写的属性,修改以后,以后绘制的label的宽度就变成了写进去的值了。不得不说,包括indentLevel在内,这些API设计的都颇有想法。
最后解决办法就是在PropertyField绘制以前,先把labelWidth改小,这样绘制出来的PropertyField前面的Label宽度就变小了。绘制完以后调回去便可。