请参阅Unity Inspector中的数组枚举索引

时间:2019-04-08 23:20:41

标签: c# unity3d

简而言之,我有一个由枚举的索引引用的数组。但是,在Unity的检查器窗口中,它仅显示“元素0,元素1等”。我希望列表显示我的枚举的索引值。除非向Unity添加了新功能,否则我认为这需要特殊的编辑器脚本,但似乎游戏代码甚至无法与编辑器代码交互。那么我该怎么做呢?预先感谢!

所以,如果我有这个:

public enum ObjectList
{
    Car,
    Sword,
    Friends,
    Depression,
    NumObjects
}

[Somehow declare to use my ObjectList Enum]
public bool [] hasItem = new bool[(int)ObjectList.NumObjects];

理想情况下,检查员现在将显示:

Has Item
    Size        4
    Car         []
    Sword       []
    Friends     []
    Depression  []

代替:

Has Item
    Size        4
    Element 0   []
    Element 1   []
    Element 2   []
    Element 3   []

编辑:附加示例为Int Array(不仅仅是bool)

Num Item
    Size        4
    Car         0
    Sword       10
    Friends     0
    Depression  50

1 个答案:

答案 0 :(得分:1)

与您描述的方式不完全相同,但已经存在一些类似的内容:

Enum Flags


我重写了一些脚本:

将此脚本放置在Assets中的任何位置,例如像Assets/Plugins/EnumFlag

EnumFlagAttribute.cs

using UnityEngine;

public class EnumFlagAttribute : PropertyAttribute
{
    public enum FlagLayout
    {
        Dropdown,
        List
    }

    public FlagLayout _layout = FlagLayout.Dropdown;

    public EnumFlagAttribute() { }

    public EnumFlagAttribute(FlagLayout layout)
    {
        _layout = layout;
    }
}

然后将此编辑器脚本复制到一个名为Editor的文件夹中(无论它放在Assets的什么位置都没有关系,只取决于名称)。 Assets/Plugins/EnumFlag/Editor→Unity会自动从最终版本中排除放置在名为Editor的文件夹中的所有脚本,因此不会因UnityEditor名称空间而导致任何错误。

EnumFlagDrawer.cs

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(EnumFlagAttribute))]
public class EnumFlagDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        if (property.propertyType == SerializedPropertyType.Enum)
        {
            switch (((EnumFlagAttribute)attribute)._layout)
            {
                case EnumFlagAttribute.FlagLayout.Dropdown:
                    property.intValue = EditorGUI.MaskField(position, label, property.intValue, property.enumNames);
                    break;

                case EnumFlagAttribute.FlagLayout.List:
                    var buttonsIntValue = 0;
                    var enumLength = property.enumNames.Length;
                    var flagSet = new bool[enumLength];

                    EditorGUI.LabelField(new Rect(position.x, position.y, EditorGUIUtility.labelWidth, EditorGUIUtility.singleLineHeight), label);
                    EditorGUI.indentLevel++;


                    var posX = position.x;
                    EditorGUI.BeginChangeCheck();
                    {
                        for (var i = 0; i < enumLength; i++)
                        {
                            position.y += EditorGUIUtility.singleLineHeight;

                            // Check if the flag is currently set
                            if (((EnumFlagAttribute.FlagLayout)property.intValue).HasFlag((EnumFlagAttribute.FlagLayout)(1 << i)))
                            {
                                flagSet[i] = true;
                            }

                            EditorGUI.PrefixLabel(new Rect(posX, position.y, EditorGUIUtility.labelWidth, EditorGUIUtility.singleLineHeight), new GUIContent(property.enumNames[i]));

                            var toogePosition = new Rect(posX + EditorGUIUtility.labelWidth, position.y, EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth, EditorGUIUtility.singleLineHeight);
                            flagSet[i] = GUI.Toggle(toogePosition, flagSet[i], property.enumNames[i]);


                            if (flagSet[i])
                            {
                                buttonsIntValue += 1 << i;
                            }
                        }
                    }
                    if (EditorGUI.EndChangeCheck())
                    {
                        property.intValue = buttonsIntValue;
                    }


                    EditorGUI.indentLevel--;

                    break;
            }
        }
        else
        {
            var color = GUI.color;
            GUI.color = new Color(1f, 0.2f, 0.2f);
            EditorGUI.LabelField(new Rect(position.x, position.y, EditorGUIUtility.labelWidth, position.height), label);
            position.x += EditorGUIUtility.labelWidth;
            EditorGUI.HelpBox(new Rect(position.x, position.y, position.width - EditorGUIUtility.labelWidth, position.height), "Use [EnumFlags] only with an enum!", MessageType.Error);
            GUI.color = color;
        }

        EditorGUI.EndProperty();
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        if (((EnumFlagAttribute)attribute)._layout == EnumFlagAttribute.FlagLayout.Dropdown)
        {
            return EditorGUIUtility.singleLineHeight;
        }


        return (property.enumNames.Length + 1) * EditorGUIUtility.singleLineHeight;
    }
}

然后在您的代码中将其用作

[System.Flags]
public enum ObjectList
{
    Car = 1 << 0,
    Sword = 1 << 1,
    Friends = 1 << 2,
    Depression = 1 << 3 
}

[EnumFlag]
public ObjectList hasItem;

这会将一个枚举字段添加到检查器中,您可以在其中(取消)检查多个值,而不是仅检查一个。

请注意,很多与此有关的教程都手动向枚举中添加了NoneAll值,但这并不是必需的,因为Unity Inspector会自动添加它们。

enter image description here

或者您可以使用我刚添加的列表布局以适合您要显示的内容

[EnumFlag(EnumFlagAttribute.FlagLayout.List)]
public ObjectList hasItem;

这会将枚举字段添加为带有相应标签的切换列表

enter image description here


在脚本中设置和读取这些按位标志的方式与通常的枚举或布尔列表有些不同:

使用按位OR |运算符设置多个值:

hasItem = ObjectList.Car | ObjectList.Sword;

// or to add a value later
hasItem |= ObjectList.Friends;

要使用按位NOT ~和AND &运算符删除某个标志

hasItem &= ~ObjectList.Car;

然后使用按位XOR ^运算符切换(反转)某个标志:

hasItem ^= ObjectList.Car;

要检查是否使用HasFlag设置了某些标志

bool hasCar = hasItem.HasFlag(ObjectList.Car);
bool hasCarOrSword = hasItem.HasFlag(ObjectList.Car | ObjectList.Sword);
bool hasCarAndSword = hasItem.HasFlag(ObjectList.Car & ObjectList.Sword);

更新

现在添加的实际上是您希望拥有一个int[]。作为与PropertyDrawer一起使用的属性,这是相当复杂的,因为afaik该属性抽屉不用于该列表/数组中的每个元素,而不是整个列表中!

但是,您可以将列表包装在一个类中,并为此创建一个PropertyDrawer:

EnumIntArray.cs

using System;

[Serializable]
public class EnumIntArray
{
    public string[] Names;
    public int[] Values;

    public EnumIntArray(Type enumType)
    {
        Names = Enum.GetNames(enumType);
        Values = new int[Names.Length];
    }
}

EnumIntArrayDrawer.cs

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(EnumIntArray), false)]
public class EnumIntArrayDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        EditorGUI.LabelField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), label);

        var values = property.FindPropertyRelative("Values");
        var names = property.FindPropertyRelative("Names");

        EditorGUI.indentLevel++;

        for (var i = 0; i < values.arraySize; i++)
        {
            var name = names.GetArrayElementAtIndex(i);
            var value = values.GetArrayElementAtIndex(i);

            position.y += EditorGUIUtility.singleLineHeight;

            var indentedRect = EditorGUI.IndentedRect(position);

            EditorGUI.LabelField(new Rect(position.x, position.y, EditorGUIUtility.labelWidth, EditorGUIUtility.singleLineHeight), name.stringValue);
            value.intValue = EditorGUI.IntField(new Rect(position.x + EditorGUIUtility.labelWidth - indentedRect.x / 2, position.y, EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth - indentedRect.x, EditorGUIUtility.singleLineHeight), value.intValue);
        }

        EditorGUI.indentLevel--;

        EditorGUI.EndProperty();
    }


    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        var values = property.FindPropertyRelative("Values");

        return (values.arraySize + 1) * EditorGUIUtility.singleLineHeight;
    }
}

,然后在您的脚本中使用它,例如

public EnumIntArray hasItems = new EnumIntArray(typeof(ObjectList));

enter image description here

以及用于访问值

var carAmount = hasItems.Values[(int)ObjectList.Car];