Multiple enum select from inspector

Hi guys I’m quite new to c# and trying to get my head round the use of enums , I’m currently using a enum for character inventory slot types which is fine for single selections eg: gloves slot, however I need to have multiple selections from the inspector for some types but it seems limited to single selection , is it not possible to have multiple enum selection on the unity inspector?

thanks :slight_smile:

You are looking for a mask:
http://docs.unity3d.com/Documentation/ScriptReference/EditorGUILayout.MaskField.html

thanks ! will have a look are there no c# examples?

That is the relevant part:

var flags : int = 0;
var options : String[] = ["CanJump", "CanShoot", "CanSwim"];

function OnGUI() {
    flags = EditorGUILayout.MaskField ("Player Flags", flags, options);
}

If you have written C# code before, it shouldn’t be an issue at all to translate it.

thanks :slight_smile: have it showing in the inspector now but just wondering how to get hold of those values on the original script or do I just override the variables? if this is right

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(UIEquipmentSlot))]
public class UIEquipmentSlotInspector : Editor
{
    public int flags = 0;
    public string[] type = {
        "Head",
        "Shoulder",
        "Shirt",
        "Chest",
        "Belt",
        "Legs",
        "Feet",
        "Wrist",
        "Gloves",
        "Back"};

    private SerializedObject uiSlot;
    private SerializedProperty
        equipment,
        slot,
        slotType;

    void OnEnable() 
    {
        uiSlot = new SerializedObject(target);
        equipment = uiSlot.FindProperty("equipment");
        slot = uiSlot.FindProperty("slot");
        slotType = uiSlot.FindProperty("slotType");
    }

    public override void OnInspectorGUI()
    {

        flags = EditorGUILayout.MaskField("Slot Types", flags, type);
        uiSlot.Update();
        EditorGUILayout.PropertyField(equipment);
        EditorGUILayout.PropertyField(slot);
        EditorGUILayout.PropertyField(slotType);
        uiSlot.ApplyModifiedProperties();
  
    }
}

Something like that possible without using editor scripts ?

Here is a simpler way - Multiple Drop Down Selection

Simple 4 Steps

Step 1 : Make a new Script “EnumFlagsAttribute”

  using UnityEngine;
  using System.Collections;
  public class EnumFlagsAttribute : PropertyAttribute
   {
       public EnumFlagsAttribute() { }
   }

Step 2 : Make another Script “EnumFlagsAttributeDrawer”

using UnityEngine;
  using System.Collections;
  using System;
  using UnityEditor;
 
  [CustomPropertyDrawer(typeof(EnumFlagsAttribute))]
   public class EnumFlagsAttributeDrawer : PropertyDrawer
   {
       public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
       {
           _property.intValue = EditorGUI.MaskField( _position, _label, _property.intValue, _property.enumNames );
       }
   }

Step 3: Enum Declaration

  [System.Flags]
      public enum FurnitureType
      {
          None , SofaPrefab , Curtains , Table , Chairs
          }
 
   [EnumFlagsAttribute]
      public  FurnitureType   enumType;

Step 4: Getting Selected Elements

List<int> ReturnSelectedElements()
      {
 
          List<int> selectedElements = new List<int>();
          for (int i = 0; i < System.Enum.GetValues(typeof(FurnitureType)).Length; i++)
          {
              int layer = 1 << i;
              if (((int) enumType & layer) != 0)
              {
                  selectedElements.Add(i);
              }
          }
 
          return selectedElements;
 
      }

Output

12 Likes

Great solution!

Good solution.
This is my version to return the elements by names (strings):

   public List<string> ReturnSelectedElements()
    {

        List<string> selectedElements = new List<string>();
     
        for (int i = 0; i < Enum.GetValues(typeof(FurnitureType)).Length; i++)
        {
            int layer = 1 << i;
            if (((int)enumType& layer) != 0)
            {
                selectedElements.Add(Enum.GetValues(typeof(enumType)).GetValue(i).ToString());
            }
        }

        return selectedElements;

    }
3 Likes

And here is a combination of Aqibsadiq and WILEz1975’s solutions in a template form:

(put this in a separate file like EditorAttributeUtils.cs or whatever)

public sealed class EnumFlagsAttribute : PropertyAttribute
{
    public EnumFlagsAttribute() { }

    public static List<int> GetSelectedIndexes<T>(T val) where T : IConvertible
    {
        List<int> selectedIndexes = new List<int>();
        for (int i = 0; i < System.Enum.GetValues(typeof(T)).Length; i++) {
            int layer = 1 << i;
            if ((Convert.ToInt32(val) & layer) != 0) {
                selectedIndexes.Add(i);
            }
        }
        return selectedIndexes;
    }
    public static List<string> GetSelectedStrings<T>(T val) where T : IConvertible
    {
        List<string> selectedStrings = new List<string>();
        for (int i = 0; i < Enum.GetValues(typeof(T)).Length; i++) {
            int layer = 1 << i;
            if ((Convert.ToInt32(val) & layer) != 0) {
                selectedStrings.Add(Enum.GetValues(typeof(T)).GetValue(i).ToString());
            }
        }
        return selectedStrings;
    }
}
#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(EnumFlagsAttribute))]
    public class EnumFlagsAttributeDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            property.intValue = EditorGUI.MaskField(position, label, property.intValue, property.enumNames);
        }
    }
#endif

How to use it:

[System.Flags]
public enum FurnitureType { None, Sofa, Curtains, Table, Chairs }

public class MyAwesomeClass : MonoBehaviour
{
    [EnumFlags]
    public FurnitureType m_furniture;

    public void Foo()
    {
        List<string> values = EnumFlagsAttribute.GetSelectedStrings(m_furniture);
        Debug.Log(string.Join(",", values));
    }
}

[EDIT - There is a mistake in this code! See my post below for the “fix”]

7 Likes

This was very helpful, do you have an example of how to set the enum from code?

Personally, I mostly just use [System.Flags] now whenever I need a multi-selection field in the inspector instead of that attribute drawer. This post started in 2013 before Unity was able to display [System.Flags] natively! Back then people had to invent that custom property, but now that there is [System.Flags], people don’t need those custom attributes much anymore. At the time when I wrote my post, I didn’t know that was the current situation.

However, you need to be aware that [System.Flags] requires that the enum be a mask. There is a mistake in my code above - sorry about that. The furniture type needs to be declared as:

[System.Flags]
public enum FurnitureMaskType { None = 0, Sofa = 1, Curtains = 1 << 1, Table = 1 << 2, Chairs = 1 << 3 }

But really that is all you need - you don’t need that custom property stuff at all! That code above will show up in the inspector as a combo-box. However, you do need to understand bitmasks.

Here’s a utility class that I often use that simplifies using bitmasks (BitmaskUtils.cs):

// functions for working with bit masks

public static class BitmaskUtils
{
    //--------------- ulong --------------------//

    // returns the mask with the specified bit set to one.
    public static ulong SetBit(ulong mask, byte index)
    {
        return (1u << index) | mask;
    }

    // returns the mask with the specified bit set to zero.
    public static ulong UnsetBit(ulong mask, byte index)
    {
        return mask & ~(1u << index);
    }

    // returns the mask with the specified bit set to one or zero based on enable.
    public static ulong ToggleBit(ulong mask, byte index, bool enable)
    {
        return enable ? SetBit(mask, index) : UnsetBit(mask, index);
    }

    // returns true if the specified bit in the given mask is one.
    public static bool IsBitSet(ulong mask, byte index)
    {
        return (mask & (1UL << index)) != 0;
    }

    // returns a mask where all the bits are set that are in either mask1 or mask2.
    public static ulong SetBits(ulong mask1, uint mask2)
    {
        return mask1 | mask2;
    }

    // returns mask1 where all the bits that are 1 in mask2 are set to 0.
    public static ulong UnsetBits(ulong mask1, uint mask2)
    {
        return mask1 & ~mask2;
    }

    // sets or unsets bits based on enable.
    public static ulong ToggleBits(ulong mask1, uint mask2, bool enable)
    {
        return enable ? SetBits(mask1, mask2) : UnsetBits(mask1, mask2);
    }

    //--------------------- uint --------------------//

    // returns the mask with the specified bit set to one.
    public static uint SetBit(uint mask, byte index)
    {
        return (1u << index) | mask;
    }

    // returns the mask with the specified bit set to zero.
    public static uint UnsetBit(uint mask, byte index)
    {
        return mask & ~(1u << index);
    }

    // returns the mask with the specified bit set to one or zero based on enable.
    public static uint ToggleBit(uint mask, byte index, bool enable)
    {
        return enable ? SetBit(mask, index) : UnsetBit(mask, index);
    }

    // returns true if the specified bit in the given mask is one.
    public static bool IsBitSet(uint mask, byte index)
    {
        return (mask & (1 << index)) != 0;
    }

    // returns a mask where all the bits are set that are in either mask1 or mask2.
    public static uint SetBits(uint mask1, uint mask2)
    {
        return mask1 | mask2;
    }

    // returns mask1 where all the bits that are 1 in mask2 are set to 0.
    public static uint UnsetBits(uint mask1, uint mask2)
    {
        return mask1 & ~mask2;
    }

    // sets or unsets bits based on enable.
    public static uint ToggleBits(uint mask1, uint mask2, bool enable)
    {
        return enable ? SetBits(mask1, mask2) : UnsetBits(mask1, mask2);
    }

    //--------------------- byte --------------------//

    // returns the mask with the specified bit set to one.
    public static byte SetBit(byte mask, byte index)
    {
        return (byte)((1u << index) | mask);
    }

    // returns the mask with the specified bit set to zero.
    public static byte UnsetBit(byte mask, byte index)
    {
        return (byte)(mask & ~(1u << index));
    }

        // returns the mask with the specified bit set to one or zero based on enable.
    public static byte ToggleBit(byte mask, byte index, bool enable)
    {
        return enable ? SetBit(mask, index) : UnsetBit(mask, index);
    }

    // returns true if the specified bit in the given mask is one.
    public static bool IsBitSet(byte mask, byte index)
    {
        return (mask & ((byte)1 << index)) != 0;
    }

    // returns a mask where all the bits are set that are in either mask1 or mask2.
    public static byte SetBits(byte mask1, byte mask2)
    {
        return (byte)(mask1 | mask2);
    }

    // returns a mask where all the bits that are one is mask2 become zero in mask1.
    public static byte UnsetBits(byte mask1, byte mask2)
    {
        return (byte)(mask1 & ~mask2);
    }

    // sets or unsets bits based on enable.
    public static byte ToggleBits(byte mask1, byte mask2, bool enable)
    {
        return enable ? SetBits(mask1, mask2) : UnsetBits(mask1, mask2);
    }
}
6 Likes

Greate solution but i belive there is some kind of mistake. Following your example i selected Sofa,Table,Chairs enums but GetSelectedStrings returned None, Curtains, Table

As I explained above, just use [System.Flags] and ignore the property attribute which was only needed before unity added System.Flags.

Ok found out there is error in your post. You can use system flags and it is OK but your example enum is wrong definided.
is should be instead (Sofa cant be just 1)

[System.Flags]
        public enum FurnitureMaskType
        {
            None = 0,
            Sofa = 1 << 1,
            Curtains = 1 << 2,
            Table = 1 << 3,
            Chairs = 1 << 4
        }

I disagree. The enum definition is 1 << n where n is incremented each time. Your mask equates to:

None,     0,      0, 0000
Sofa,     1 << 1, 2, 0010 <-- you skipped 0001 before this
Curtains, 1 << 2, 4, 0100
Chairs,   1 << 3, 8, 1000, etc...

You should not skip 0001 (which is the same as 1 << 0, which is the same as 1). This will probably still work the way you have it, but you’re wasting a perfectly good bit out of the 32 bits available.