【Unity】在Inspector上显示自定义的位掩码枚举(Flags)

前面啰嗦了踩坑过程,想看源码直接拉到最后。。。

IMGUI 实现,版本原因,没有查看 UIElements

Unity编辑器默认并不支持将 Flags 枚举以位掩码形式显示在Inspector上,像Layers这些控件,并不通用。

想让所有带有 Flags 特性的枚举都能支持多选,就要自定义编辑器扩展。然后,坑就来了!


坑1:EditorGUI.MaskField()

EditorGUI.MaskField() ,这个方法一看名字,就感觉是我需要的,然后我写出了这玩意:

// ...
property.intValue = EditorGUI.MaskField(position, label, property.intValue, property.enumDisplayNames);
// ...

Inspector成功地以位掩码形式显示出了 Flags 枚举,但是,用着用着感觉不对啊!它错位了!

问题在于,这个方法返回的不是所选的枚举的总值,而是所选的枚举项的索引,也就是说,他表示你选了哪几个枚举项。

这个方法的文档里,对返回值的说明是“The value modified by the user”,我觉得他应该改成“The indexes selected by the user”。

所以,如果要用这种方法正确地实现需求,要手动去获得 当前所选枚举值每个枚举项的值每个枚举项的名称,然后通过 位运算 手动去计算 当前选择了哪些枚举项 ,再调用 MaskField() 方法绘制控件。然后再拿到代表 当前所选枚举项索引 的返回值,通过 位运算 手动去计算 所选的索引对应的最终枚举值

我这样做了下,确实是能正确显示了,但是不支持组合掩码(AB=A|B这种,见示例),不支持1<<31(见示例)。嫌烦没有去处理这个问题,所以查了下有没有其他方式实现,然后遇到了坑2。


坑2:EditorGUI.EnumFlagsField()

EnumFlagsField() 方法是这样用的:

Enum currentEnum = (Enum)fieldInfo.GetValue(property.serializedObject.targetObject);
Enum newEnum = EditorGUI.EnumFlagsField(position, label, currentEnum);
property.intValue = Convert.ToInt32(newEnum);

很简单,完美地解决了坑1中的所有问题。

但是,当把 Flags 枚举作为某个类型的字段,然后这个类型又在某个 ScriptableObject 中作为 数组成员 时,上述代码就会抛出异常:ArgumentException: Field <enum_flags> defined on type <host_type> is not a field on the target object which is of type <scriptable_object>.

问题出在 property.serializedObject.targetObject 上,它有些乱七八糟的归属问题。


解决归属问题

偷懒了,没解决,我把它绕过去了。

既然 property.serializedObject.targetObject 有归属问题,那么不通过这个对象获取枚举值就好了。但是我找了一圈,也没找到 通过枚举类型和枚举值来生成Enum对象 的方法。

这时我的想法是,看看 EnumFlagsField() 的内部是怎么实现的,然后照搬它的方式重新填一下坑1,于是反编译了 EnumFlagsField() 的代码。

反编译之后,在 EnumFlagsField() 的实现中有个意外的发现:EditorGUI 类型中含有个内部静态方法 IntToEnumFlags(),可以通过枚举类型和枚举值来生成Enum对象,那么,有了这个方法,又可以回到坑2中来了!

用反射,拿到这个方法,然后生成枚举对象,代码变成了这样:

MethodInfo miIntToEnumFlags = typeof(EditorGUI).GetMethod("IntToEnumFlags", BindingFlags.Static | BindingFlags.NonPublic);
Enum currentEnum = miIntToEnumFlags.Invoke(null, new object[] { fieldInfo.FieldType, property.intValue });
Enum newEnum = EditorGUI.EnumFlagsField(position, label, currentEnum);
property.intValue = Convert.ToInt32(newEnum);

到此,坑填好了,所有问题已解决!(PS:其实我没特别全面地进行测试。。。)


完整代码

using System;
using UnityEngine;/// <summary>
/// 将枚举以位掩码的形式显示在Inspector上。
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class ShowAsFlagsAttribute : PropertyAttribute
{}
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;/// <summary>
/// 自定义属性绘制器,将枚举以位掩码的形式显示在Inspector上。
/// </summary>
[CustomPropertyDrawer(typeof(ShowAsFlagsAttribute))]
public class ShowAsFlagsDrawer : PropertyDrawer
{private MethodInfo _miIntToEnumFlags;public override void OnGUI(Rect position, SerializedProperty property, GUIContent label){// 如果不是枚举,则按默认显示if (property.propertyType != SerializedPropertyType.Enum){EditorGUI.PropertyField(position, property);return;}if (_miIntToEnumFlags == null){_miIntToEnumFlags = typeof(EditorGUI).GetMethod("IntToEnumFlags", BindingFlags.Static | BindingFlags.NonPublic);}// 复杂的转换问题,让Unity来解决(参考EditorGUI.EnumFlagsField()方法的反编译结果)Enum currentEnum = (Enum)_miIntToEnumFlags.Invoke(null, new object[] { fieldInfo.FieldType, property.intValue });EditorGUI.BeginProperty(position, label, property);Enum newEnum = EditorGUI.EnumFlagsField(position, label, currentEnum);property.intValue = Convert.ToInt32(newEnum);EditorGUI.EndProperty();// 备注:// 不能使用以下方式获取枚举值:// Enum currentEnum = (Enum)fieldInfo.GetValue(property.serializedObject.targetObject);// 使用以下方式时,如果ScriptableObject中包含一个某类型的数组,该类型中包含了Flags枚举,将会导致Editor抛出ArgumentException:// ArgumentException: Field <enum_flags> defined on type <host_type> is not a field on the target object which is of type <unity_object>.}
}

使用示例

using UnityEngine;[System.Flags]
public enum MyFlags
{A = 1 << 0,B = 1 << 2,Z = 1 << 31,AB = A | B,AZ = A | Z,
}public class Test : MonoBehaviour
{[ShowAsFlags] // 这样用public MyFlags flags;
}

【Unity】在Inspector上显示自定义的位掩码枚举(Flags)相关推荐

  1. Elasticsearch:使用反向地理编码在地图上显示自定义区域统计数据

    在实际的许多应用中,我们可能并不一定按照行政区来进行划分区域,比如我们常说江浙一代,我们可以理解江苏和浙江这两个省合在一起,而不是把它们分开.我们有时也说长江三角区,它可能是跨几个省市的一个区域,而不 ...

  2. ECharts如何制作省份地图并在地图上显示自定义图标/散点图

    之前一直被客户提的一个需求烦恼着,他要只显示某个省份的地图,并且呢要在地图上显示小车的图标,还有其它一些点的信息.我以前只用过echarts做过图表,没做过地图,所以自己也是网上看了很多并且看了很多a ...

  3. python使用matplotlib对比多个模型在测试集上的效果并可视化、设置模型性能可视化结果柱状图(bar plot)标签的小数点位数(例如,强制柱状图标签0.7显示为两位小数0.70)

    python使用matplotlib对比多个模型在测试集上的效果并可视化.设置模型性能可视化结果柱状图(bar plot)标签的小数点位数(例如,强制柱状图标签0.7显示为两位小数0.70) 目录

  4. Unity摄像机拍照并显示在小窗口里(RenderTexture和RawImage将摄像机图像渲染到GUI屏幕上小图显示)

    Unity摄像机拍照并显示在小窗口里(RenderTexture和RawImage将摄像机图像渲染到GUI屏幕上小图显示) 1.实现效果和实现思想 1.实现效果和实现思想 实现效果: 本文使用飞机模型 ...

  5. MPAndroidchart自定义样式二在柱状图上显示文本和间断式显示柱状图

    内容描述 a .在柱状图上显示文本 b.间断式显示柱状图 产品原型如下: 难点描述: MPAndroidChart 并不支持将文字描述信息展示到柱状图(条形图)上:而且也不支持不从0开始的柱状节点展示 ...

  6. Unity 在设备上材质显示正常,但是Editor下材质显示为紫色

    windows 下面bundle模式紫色图片解决   运行平台还是原来的 android 或 ios, 但是 PC Editor 的渲染引擎要跟平台的一致起来 Unity 在设备上材质显示正常,但是E ...

  7. 在六位共阴数码管上最左边一位上显示稳定的数字

    解析 通过P0口对数码管进行位选和段选,由于只使用了一个IO口,为避免数据混乱,通过两个锁存器进行位选和段选的控制: while(1)循环中,先打开了位选端,并通过P0口发送数据0XFE,此时发送的数 ...

  8. Unity 在web上材质显示正常,但是unity端材质显示为紫色

    问题描述: Addressable打包后的物体,在web端显示正常,但是在unity端材质就显示紫色 解决方案: 和windows端的图形API有关 Player Setting中进行设置 选择win ...

  9. Unity中将3D模型显示在UI上或者显示在UI前面

    标题 Unity中将3D模型显示在UI上或者显示在UI前面 一.将3D模型显示在UI上-使用RawImage和Render Texture组合实现效果 1.创建一个RawImage控件 2.创建一个R ...

  10. unity怎么在UI面板上显示出3D立体模型

    unity怎么在Ui面板上显示出3D模型! 1.创建Render texture 2.创建RawImage 3.创建摄像机 注意点:1,摄像机和RawImage上面要将Render texture拖上 ...

最新文章

  1. BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )
  2. 德布鲁因图和OLC组装基因组
  3. linux批量部署war工具,Linux批量部署工具Expect
  4. 2016年工作中遇到的问题31-40
  5. oracle报错无效列类型,jooq oracle存储过程与ARRAY,无效列类型:1111
  6. Android NDK学习笔记1:基础
  7. 使用Spring将POJO公开为JMX MBean
  8. spring security method security
  9. NS2相关学习——无线网(2)
  10. Gartner:克服SIEM部署失败的通病
  11. Ubuntu编译内核及grub的一些笔记
  12. python 安装包查询_Linux系统下查找安装包所在目录
  13. 密码学是如何保护区块链的 1
  14. Hyperledger Fabric教程(11)-- 链码和背书策略
  15. VS2010 VB.net安装包生成过程
  16. python学习手册 简记
  17. 上位机软件系统开发工具简介
  18. linux 交换机实例,华为路由器和交换机实例配置
  19. Web安全——文件包含漏洞
  20. 创意电子学-小知识:研究继电器

热门文章

  1. 机器学习实战——xgboost股票close预测
  2. matlab编译后方交会,后方交会MATLAB程序实习报告.docx
  3. 明白这些Python 常用包的用法,想不学会Python都难
  4. OpenCV学习笔记_图像扭曲及旋转操作
  5. 国内外需求管理工具使用感悟!
  6. 大型网站技术架构+核心原理与案例分析+李智慧
  7. 西方红玫瑰和辣条先生黑产组织深度分析报告
  8. WIN7常用使用设置
  9. 疫情中的云与教育:“停课不停学”背后的百度技术密码
  10. 完美解决Can‘t locate Data/Dumper.pm in @INC