Manage lots of Enum choices in the Inspector

I’m writing a RPG, and to keep track of individual item types (ex: each weapon, each resource &c), I use an enum.

At the moment I have 90 or so entries, and so when I need to find a specific item type in the inspector, it’s a bit of a headache. The inspector supports searching by the first character (and only that), but that’s not that helpful

Is there a way of managing enum values in the inspector - such as being able to group them, or get a better search instead of going through the dropdown?

2 Answers

2

Using an enum might not be the best solution in your case. But I don’t know your system enough to judge.

Anyway, InspectorName attribute lets you group enum values.

public enum Item
{
    [InspectorName("Weapon/Melee/Sword")]
    Sword,
    [InspectorName("Weapon/Melee/Axe")]
    Axe,
    [InspectorName("Weapon/Ranged/Bow")]
    Bow,
    [InspectorName("Weapon/Magic/Wand")]
    Wand,
    [InspectorName("Weapon/Magic/Staff")]
    Staff,
    [InspectorName("Equipment/Helmet")]
    Helmet,
    [InspectorName("Equipment/Armor")]
    Armor,
    [InspectorName("Consumable/HealthPotion")]
    HealthPotion,
    [InspectorName("Consumable/ManaPotion")]
    ManaPotion,
    [InspectorName("Consumable/Food")]
    Food,
    Coin
}

188120-enuminspectorname.png


If you want the enum to be searchable, you’ll need more code:

// SearchableAttribute.cs in your Assets folder

using UnityEngine;

public class SearchableAttribute : PropertyAttribute { }

// SearchableAttributeDrawer.cs >>> IN AN 'Editor' FOLDER <<<

using System;
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(SearchableAttribute))]
public class SearchableAttributeDrawer : PropertyDrawer
{
    private string search;
    private string[] options;
    private GUIStyle searchTextFieldStyle;

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        float height = base.GetPropertyHeight(property, label);

        if (property.propertyType == SerializedPropertyType.Enum)
            height = height * 2 + EditorGUIUtility.standardVerticalSpacing;

        return height;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (property.propertyType == SerializedPropertyType.Enum)
        {
            if (searchTextFieldStyle == null)
                searchTextFieldStyle = GUI.skin.FindStyle("ToolbarSeachTextField");

            if (options == null)
                UpdateOptions(property.enumDisplayNames);

            Rect searchRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
            DrawSearchBar(searchRect, label, property.enumDisplayNames);

            Rect popupRect = new Rect(position.x, searchRect.y + searchRect.height + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);
            DrawEnumPopup(popupRect, property);
        }
        else
        {
            EditorGUI.PropertyField(position, property, label);
        }
    }

    private void DrawSearchBar(Rect position, GUIContent label, string[] allOptions)
    {
        EditorGUI.BeginChangeCheck();
        search = EditorGUI.TextField(position, label, search, searchTextFieldStyle);
        if(EditorGUI.EndChangeCheck())
            UpdateOptions(allOptions);
    }

    private void DrawEnumPopup(Rect position, SerializedProperty property)
    {
        Rect fieldRect = EditorGUI.PrefixLabel(position, new GUIContent(" "));
        int currentIndex = Array.IndexOf(options, property.enumDisplayNames[property.enumValueIndex]);
        int selectedIndex = EditorGUI.Popup(fieldRect, currentIndex, options);
        if (selectedIndex >= 0)
        {
            int newIndex = Array.IndexOf(property.enumDisplayNames, options[selectedIndex]);
            if (newIndex != currentIndex)
            {
                property.enumValueIndex = newIndex;
                search = string.Empty;
                UpdateOptions(property.enumDisplayNames);
            }
        }
    }

    private void UpdateOptions(string[] allOptions)
    {
        options = Array.FindAll(allOptions, name => string.IsNullOrEmpty(search) || name.IndexOf(search, StringComparison.InvariantCultureIgnoreCase) >= 0);
    }
}

// In your code

[Searchable]
public Item Item;

The inspector name is incredibly helpful. Thank you so much. Not sure about the enums myself either, but if I need to make references in crafting (ex: sword needs 5 ingots, and 3 coal), I thought enums would be the safest way to keep references.

Note that: "ToolbarSeachTextField" is not a typo, it can be found in the Unity C# source code: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/GUI/EditorStyles.cs

In this case, I would probably use ScriptableObject instances instead of enum. Not just that it can be used to identify item type, it can also contain its data (inventory icon?) and can be added without changing the code.

this tool might help: