Короче говоря, у меня есть массивы, на которые ссылается индекс из перечисления. Однако в окне инспектора 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
Примечание: для размера вашего массива вы должны использовать Enum.GetNames(typeof(MyEnum)).Length;, использование последнего элемента перечисления - это что-то вроде взлома, и учитывая, что он не должен быть элементом массива. .. ну вы поняли идею.
@Everts Думаю, это то, что я искал ранее, но был потрясен, не найдя простого enum.length. Похоже, что таким образом могут возникнуть дополнительные накладные расходы, хотя необходимо создать целый массив имен строк только для того, чтобы задать его длину. Разве массив уже не существует?
на самом деле он создает новый массив, когда вы звоните, но это один раз, поэтому я бы не стал об этом беспокоиться.
Не совсем так, как вы это описываете, но что-то очень похожее уже существует:
Немного переписал скрипты:
Поместите этот скрипт в любое место в 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?
на самом деле не так просто, по крайней мере, не как атрибут... см. мое обновление
Это кажется настолько близким, насколько я могу получить то, что я хочу. Я надеялся, что мне не придется менять типы данных, но я попробую! Спасибо!
Аналогичный вопрос был задан на forum.unity.com/threads/…. Вам нужно будет создать собственный инспектор, вот хороший туториал от Unity