在实现SceneName之后,我又给enum写了一个标签,这个标签的功能是使enum能够像LayerMask一样多选。
实现
这个需求比较直观,而且直接调函数就能实现功能,所以这里直接贴代码。
[AttributeUsage(AttributeTargets.Field)]
public class MultiSelector : PropertyAttribute
{
public MultiSelector() {
}
}
[CustomPropertyDrawer(typeof(MultiSelector))]
public class MultiSelectorEditor : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Enum targetObject = fieldInfo.GetValue(property.serializedObject.targetObject) as Enum;
targetObject = EditorGUI.EnumFlagsField(position, label, targetObject);
fieldInfo.SetValue(property.serializedObject.targetObject, targetObject);
if(GUI.changed)
{
EditorUtility.SetDirty(property.serializedObject.targetObject);
}
}
}
它的用法就是标记一个枚举变量,让它在编辑器里支持多选。
当然,这个实现是早期的实现,它和当时SceneName的写法是相同的。因此,它也存在初版SceneName所存在的问题,也就是如果被嵌在了另一个类里,那么在绘制时就会有问题。
原因在那篇文章里也大致解释过了,就是fieldInfo无法根据指定的名称获取到变量。
改进
因为改这个MultiSelector是在改SceneName之后,所以这次修改的方向就很清晰了:改用property提供的属性来访问枚举的值即可。
在property中提供了enumValueFlag这个属性来获得枚举的值。或者直接用intValue,得到的值也是一样的。
但是现在又有一个问题……
新的问题
EnumFlagsField是针对Enum的操作,而我们能获取到的是一个int值。
直接做类型转换当然是首先想到的方案,但是int是不能向Enum转换的,直接就会报错。
我也试了先转object再转Enum,但是也不能达到预期。
所以现在如何进行类型转换又成为了亟待解决的问题。
一个没有成功的思路
虽然Enum和int之间不能互相转换,但对于一个具体的枚举类型来说,可以直接进行类型的转换。
基于这个思路,我创建了一个有32个值的枚举类型,每个值对应32位二进制数的一位,这样就可以利用这个类型进行类型转换。
public enum Enum32
{
e0=1<<0,
e1=1<<1,
...
e30=1<<30,
e31=1<<31,
}
但是由于转换后得到的值是Enum32类型,所以EnumFlagsField绘制的下拉框的值列表是Enum32的这些值。
虽然类型的问题是解决了,但这自然也是不行的,所以还需要再寻找其它方法。
一个比较成功的思路
在上述的尝试失败后,我又发现Enum提供了ToObject方法,它可以将一个整型值转成一个枚举。
所以现在的问题就转化为获取被修饰字段的类型。
获取类型
fieldInfo存储的是字段的信息,所以要获取字段的类型,应该是可以从这里入手的。
FieldType就是我们需要的类型。
综上,最终的写法如下:
[CustomPropertyDrawer(typeof(MultiSelector))]
public class MultiSelectorEditor : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Enum targetObject = Enum.ToObject(fieldInfo.FieldType, property.enumValueFlag) as Enum;
targetObject = EditorGUI.EnumFlagsField(position, label, targetObject);
property.enumValueFlag = Convert.ToInt32(targetObject);
if(GUI.changed)
{
EditorUtility.SetDirty(property.serializedObject.targetObject);
}
}
}