Смотрите перечисленные индексы массива в Unity Inspector - PullRequest
1 голос
/ 09 апреля 2019

Короче говоря, у меня есть массивы, на которые ссылается индекс из перечисления. Однако в окне инспектора 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 (не просто bool)

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

1 Ответ

1 голос
/ 09 апреля 2019

Не совсем так, как вы это описываете, но что-то очень похожее уже существует:

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 & rightarrow; 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;

Это добавляет поле enum к вашему Инспектору вместо того, чтобы вы могли (не) проверять несколько значений вместо только одного.

Обратите внимание, что многие учебники по этому вопросу вручную добавляют значения None и All к перечислению, но это не нужно, поскольку Инспектор Unity добавляет их автоматически.

enter image description here

или вы можете использовать макет списка, который я только что добавил, чтобы соответствовать тому, что вы хотели отобразить

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

Это добавляет поле enum в виде списка переключателей с соответствующими метками вместо

enter image description here


Установка и чтение этих побитовых флагов в скрипте работает немного иначе, чем обычный список enum или bool:

Установка нескольких значений с помощью побитового оператора ИЛИ |:

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);

UPDATE

Как вы добавили сейчас, вы бы предпочли иметь int[]. Это довольно сложно в качестве атрибута с PropertyDrawer, так как afaik свойство drawdrawer используется для каждого элемента в этом списке / массиве, а не во всем списке!

Однако вы можете вместо этого обернуть список в класс и создать вместо него 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];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...