一般来说,当我们要扩展编辑器时,我们会从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. phpshell提权

    实际操作中可以在webshell用udf.dll提权,用函数的上传文件功能上传文件到启动目录,再用shut函数重起系统.(目前没成功过,有 机会本地测试一下,先记录在这了).如果是英文版的系统,启动目 ...

  2. Linux防火墙iptables学习

    http://blog.chinaunix.net/uid-9950859-id-98277.html 要在网上传输的数据会被分成许多小的数据包,我们一旦接通了网络,会有很多数据包进入,离开,或者经过 ...

  3. Sqoop使用笔记(转载)

    Sqoop是Apache顶级项目,主要用来在Hadoop和关系数据库中传递数据.通过sqoop,可以方便的将数据从关系数据库导入到HDFS,或将数据从HDFS导出到关系数据库. 关于Sqoop 官网S ...

  4. (转)NEST.net Client For Elasticsearch简单应用

    由于最近的一个项目中的搜索部分要用到 Elasticsearch 来实现搜索功能,苦于英文差及该方面的系统性资料不好找,在实现时碰到了不少问题,现把整个处理过程的代码分享下,给同样摸索的人一些借鉴,同 ...

  5. 【转】关于增量链接(incremental linking)

    增量链接(Incremental Linking)这个词语在使用Visual C++时经常会遇到(其实不只是VS系列,其它链接器也有这个特性), 就比如经常遇到的:上一个增量链接没有生成它, 正在执行 ...

  6. New API

    New API Producer >增加发送回调 >重构Partition 统一High Level API与Low Level API >从kafka.consumer和kafka ...

  7. ADO.NET基础必备之SqlCommand.Execute三方法

    SqlCommand.ExecuteNonQuery 方法   对连接执行 Transact-SQL 语句并返回受影响的行数. ――语法: public override int ExecuteNon ...

  8. Linux命令发送Http GET/POST请求

    Get请求 curl命令模拟Get请求: 1.使用curl命令: curl "http://www.baidu.com" 如果这里的URL指向的是一个文件或者一幅图都可以直接下载到 ...

  9. VBA 实现学校上课教员一学期中所有上课时间,在一页中通过背景底色反应出来

    需求:学校一学期的所有课程表,每个教员都有可能上好几门课,但给一个教员调课时需要查找所调课时间位置有没有此教员上其它的课 相冲突,手动查找很不方便,这里想通过一个表中位置显示出同一教员在所有课表中出现 ...

  10. 【EF】Entity Framework Core 2.0 特性介绍和使用指南

    阅读目录 前言 获取和使用 新特性 项目升级和核心API变化 下一步计划 遗憾的地方 回到目录 前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升 ...