Не совсем так, как вы это описываете, но что-то очень похожее уже существует:
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 добавляет их автоматически.

или вы можете использовать макет списка, который я только что добавил, чтобы соответствовать тому, что вы хотели отобразить
[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);
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));

и для доступа к значениям
var carAmount = hasItems.Values[(int)ObjectList.Car];