一般来说,当我们要扩展编辑器时,我们会从Editor类继承,为自己的MonoBehaviour实现不同的外观。

但是如果有一个struct/class,在许多地方被使用,Unity默认的外观又不够好看,此时想修改它的外观,就需要使用PropertyDrawer了。



上图是一个Monobehaviour中包含一个简单的struct(TileCoord类),包含两个int,但是显示效果十分别扭。



实现对应的PropertyDrawer后

相对于Editor类可以修改MonoBehaviour的外观,我们可以简单的理解PropertyDrawer为修改struct/class的外观的Editor类。

实现上面的效果的代码如下

[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一个方法来自定义高度,否则默认为一行高。

property:待绘制的属性本身。Unity在编辑器的API中大部分的实际的值都是用一个SerializedProperty表示的,实际上就是对值的一个包装。通过这个包装,当我们修改值的时候,Unity可以知道这次操作,类似刷新界面、Undo、prefab修改之类的信息都可以帮我们处理好。坏处在于我们得通过类似FindPropertyRelative的方法,用字符串去寻找内部的值(SerializedProperty是个嵌套结构,内部的数据也是SerializedProperty)。在Unity升级C#来支持nameof之前,我们只能尽量避免修改字段的名字了。同时,我们绘制这些property的时候可以直接用EditorGUI.PropertyField(property),而不用类似的 x = EditorGUI.IntField(x)这样的调用。

label:这个值在MonoBehaviour里的字段名。

另外,在PropertyDrawer中不能使用带Layout的类,即EditorGUILayout、GUILayout。( http://answers.unity3d.com/questions/661360/finally-a-solution-cant-use-guilayout-stuff-in-pro.html )用了的话会报个迷之错误。不过似乎并不是bug,而是设计如此(不允许PropertyDrawer用Layout)。

最后说一下EditorGUIUtility.labelWidth的使用。

我在最开始实现这个PropertyDrawer时,代码如下

    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宽度就变小了。绘制完之后调回去即可。

[Unity]用PropertyDrawer自定义struct/class的外观的更多相关文章

  1. Unity 编辑器扩展自定义窗体

    这次看见Unity还可以自定义弹出窗体,让我很好奇.于是就去网上找文章看了看. 如果想自定义窗体需要把类放入Editor文件夹下面. 代码如下: using UnityEngine; using Un ...

  2. 自定义QT窗口部件外观之QStyle

    自定义QT窗口部件外观 重新定义Qt内置窗口部件的外观常用的方法有两种:一是通过子类化QStyle 类或者预定义的一个样式,例如QWindowStyle,来定制应用程序的观感:二是使用Qt样式表. Q ...

  3. 【Unity Shader】自定义材质面板的小技巧

    写在前面 之前遇到过一些朋友问怎么在材质面板里定义类似于bool这种变量,控制一些代码的执行.我们当然可以写一个C#文件来自定义材质面板,就像Unity为Standard Shader写材质面板一样( ...

  4. Unity 扩展属性自定义绘制

    这么晚了准备睡觉的时候,去学习了一会. 发现一个标题好奇的点进去. 居然是自定义绘制属性.  在前几天这个问题把我难住了,没想到几分钟就能解决的问题. 我花了半天时间使用反射去解决...  如果我们想 ...

  5. Unity编辑器:自定义编辑器样式——GUIStyle

    通过GUIStyle,可以自定义Unity编辑器的样式. GUIStyle可以new一个全新的实例,这样,需要自己处理所有自己需要的效果. GUIStyle还可以基于已经存在的实例new一个新的实例, ...

  6. golang自定义struct字段标签

    原文链接: https://sosedoff.com/2016/07/16/golang-struct-tags.html struct是golang中最常使用的变量类型之一,几乎每个地方都有使用,从 ...

  7. 2、以自定义struct或struct指针作为map的Key

    若干问题: struct Node { int k, b; friend bool operator <(Node a, Node b) { return a.k < b.k; } }no ...

  8. unity中使用自定义shader进行光照贴图烘培无法出现透明度的坑爹问题

    最近开发中在对场景进行光照贴图烘焙时发现一个坑爹问题,在使用自定义shader的时候,shader命名中必须包含Transparent路径,否则烘焙的时候不对alpha通道进行计算,烘焙出来都是狗皮膏 ...

  9. 【Android】9.3 自定义列表视图的外观

    分类:C#.Android.VS2015: 创建日期:2016-02-18 一.简介 自定义的列表视图通常用Resources/Layout文件夹下的axml文件中的资源来声明,适配器则通过Id去加载 ...

随机推荐

  1. SQL SERVER技术内幕之7 透视与逆透视

    1.透视转换 透视数据(pivoting)是一种把数据从行的状态旋转为列的状态的处理,在这个过程中可能须要对值进行聚合. 每个透视转换将涉及三个逻辑处理阶段,每个阶段都有相关的元素:分组阶段处理相关的 ...

  2. 【python】如何查看已经安装的python软件包和版本

    pip 是一个安装和管理 Python 包的工具 , 是 easy_install 的一个替换品. pip freeze可以查看已经安装的python软件包和版本 pip list 也可以

  3. BZOJ 1010 玩具装箱(斜率优化DP)

    dp[i]=min(dp[j]+(sum[i]-sum[j]+i-j-1-L)^2) (j<i) 令f[i]=sum[i]+i,c=1+l 则dp[i]=min(dp[j]+(f[i]-f[j] ...

  4. CodeForces 632E Thief in a Shop

    题意:给你n种物品,每种无限个,问恰好取k个物品能组成哪些重量.n<=1000,k<=1000,每种物品的重量<=1000. 我们搞出选取一种物品时的生成函数,那么只要对这个生成函数 ...

  5. BZOJ4245 ONTAK2015 OR-XOR(贪心)

    贪心的按位考虑.如果所有数在某一位上有奇数个为1,显然无论如何划分这一位最终都会为1:否则将每一部分都划分为偶数个1就能保证最终该位为0,可以标记上哪些位置可以作为划分点(当然也要满足之前可为0的位上 ...

  6. 算法语言Scheme修订6报告 R6RS简体中文翻译

    算法语言Scheme修订6报告 R6RS简体中文翻译 来源 https://r6rs.mrliu.org/   MICHAEL SPERBERR. KENT DYBVIG, MATTHEW FLATT ...

  7. BZOJ4872:[SHOI2017]分手是祝愿——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=4872 https://www.luogu.org/problemnew/show/P3750 Zei ...

  8. ZOJ1937:Addition Chains——题解

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1937 题目大意:创造一个数列,使得它: 1.单调不递减. 2.其中一个元素 ...

  9. Python3简单入门

    在Mac和Linux上运行Python时,请打开终端,然后运行python3 Mac OSX 正确地同时安装Python 2.7 和Python3:  http://www.jianshu.com/p ...

  10. selenium - webdriver - ActionChains类(鼠标操作)

    ActionChains 类提供了鼠标操作的常用方法: perform(): 执行所有 ActionChains 中存储的行为: context_click(): 右击: double_click() ...