См. перечисляемые индексы массива в инспекторе Unity

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

Аналогичный вопрос был задан на forum.unity.com/threads/…. Вам нужно будет создать собственный инспектор, вот хороший туториал от Unity

Aiden Faulconer 09.04.2019 05:44

Примечание: для размера вашего массива вы должны использовать Enum.GetNames(typeof(MyEnum)).Length;, использование последнего элемента перечисления - это что-то вроде взлома, и учитывая, что он не должен быть элементом массива. .. ну вы поняли идею.

Everts 09.04.2019 12:48

@Everts Думаю, это то, что я искал ранее, но был потрясен, не найдя простого enum.length. Похоже, что таким образом могут возникнуть дополнительные накладные расходы, хотя необходимо создать целый массив имен строк только для того, чтобы задать его длину. Разве массив уже не существует?

CodeMonkey 09.04.2019 13:30

на самом деле он создает новый массив, когда вы звоните, но это один раз, поэтому я бы не стал об этом беспокоиться.

Everts 09.04.2019 14:11
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
1 302
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Флаги перечисления


Немного переписал скрипты:

Поместите этот скрипт в любое место в 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;

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

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

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

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

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


Установка и чтение этих побитовых флагов в скрипте работает немного иначе, чем обычный список 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);

ОБНОВИТЬ

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

Однако вместо этого вы можете обернуть список в класс и вместо этого создать для этого 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));

и для доступа к значениям

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

Спасибо за ответ. Я не осознавал, что мне нужно будет изменить свое перечисление на BitMask, чтобы добиться этого. Проблема в том, что у меня также есть другие массивы, такие как массив int, для которых я надеялся использовать то же решение. Любой способ сделать это для int [] numItems?

CodeMonkey 09.04.2019 12:39

на самом деле не так просто, по крайней мере, не как атрибут... см. мое обновление

derHugo 09.04.2019 14:04

Это кажется настолько близким, насколько я могу получить то, что я хочу. Я надеялся, что мне не придется менять типы данных, но я попробую! Спасибо!

CodeMonkey 09.04.2019 18:37

Другие вопросы по теме