Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
14
Question by AlwaysSunny · Feb 05, 2013 at 08:39 AM · inspectorcustom inspector

custom inspector multi-select enum dropdown?

I want to make a drop-down in a custom inspector that allows me to select multiple enums from a popup, exactly like the popups you get when selecting a camera's culling mask, a projector's "ignore" layers, or a layermask. (see picture)

Am I missing a simple way to do this? What would the returned result of a change be? A List or array of FooEnum elements? I can jury-rig an equivalent alternative, but since this GUI element does exactly what I want, I'd love to use one if I could find out how!

alt text

flags.jpg (82.1 kB)
Comment
Add comment · Show 1
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image yoyo · Feb 27, 2014 at 02:54 AM 0
Share

Nice simple solution with PropertyDrawer + $$anonymous$$askField here: http://answers.unity3d.com/questions/486694/default-editor-enum-as-flags-.html

4 Replies

· Add your reply
  • Sort: 
avatar image
32
Best Answer

Answer by Bunny83 · Feb 05, 2013 at 03:24 PM

I've written a "wrapper" function, mainly for a general PropertyDrawer, which actual uses the enum values. However, this changes the Nothing and Everything behaviour, but all enum values work fine, even combined values.

 //C#
 
 // Have to be defined somewhere in a runtime script file
 public class BitMaskAttribute : PropertyAttribute
 {
     public System.Type propType;
     public BitMaskAttribute(System.Type aType)
     {
         propType = aType;
     }
 }

This should go into an editor script:

 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 
 public static class EditorExtension
 {
     public static int DrawBitMaskField (Rect aPosition, int aMask, System.Type aType, GUIContent aLabel)
     {
         var itemNames = System.Enum.GetNames(aType);
         var itemValues = System.Enum.GetValues(aType) as int[];
         
         int val = aMask;
         int maskVal = 0;
         for(int i = 0; i < itemValues.Length; i++)
         {
             if (itemValues[i] != 0)
             {
                 if ((val & itemValues[i]) == itemValues[i])
                     maskVal |= 1 << i;
             }
             else if (val == 0)
                 maskVal |= 1 << i;
         }
         int newMaskVal = EditorGUI.MaskField(aPosition, aLabel, maskVal, itemNames);
         int changes = maskVal ^ newMaskVal;
         
         for(int i = 0; i < itemValues.Length; i++)
         {
             if ((changes & (1 << i)) != 0)            // has this list item changed?
             {
                 if ((newMaskVal & (1 << i)) != 0)     // has it been set?
                 {
                     if (itemValues[i] == 0)           // special case: if "0" is set, just set the val to 0
                     {
                         val = 0;
                         break;
                     }
                     else
                         val |= itemValues[i];
                 }
                 else                                  // it has been reset
                 {
                     val &= ~itemValues[i];
                 }
             }
         }
         return val;
     }
 }
 
 [CustomPropertyDrawer(typeof(BitMaskAttribute))]
 public class EnumBitMaskPropertyDrawer : PropertyDrawer
 {
     public override void OnGUI (Rect position, SerializedProperty prop, GUIContent label)
     {
         var typeAttr = attribute as BitMaskAttribute;
         // Add the actual int value behind the field name
         label.text = label.text + "("+prop.intValue+")";
         prop.intValue = EditorExtension.DrawBitMaskField(position, prop.intValue, typeAttr.propType, label);
     }
 }

Now just imagine this example:

 public enum EMyEnum
 {
     None         = 0x00,
     Cow          = 0x01,
     Chicken      = 0x02,
     Cat          = 0x04,
     Dog          = 0x08,
     CowChicken   = 0x03,
     CatDog       = 0x0C,
     All          = 0x0F,
 }
 
 public class Example : MonoBehaviour
 {
     [BitMask(typeof(EMyEnum))]
     public EMyEnum someMask;
 }

This will automatically draw my modified enum-mask field.

Comment
Add comment · Show 18 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Feb 05, 2013 at 03:26 PM 0
Share

unfortunately there is no way (i know) to deter$$anonymous$$e the actual type of the SerializedProperty, so i have to pass the type as attribute parameter.

avatar image AlwaysSunny · Feb 07, 2013 at 06:20 AM 0
Share

$$anonymous$$y solution for achieving this functionality would've been much more amateurish. This one's a keeper, many thanks for sharing your code!

avatar image Ellandar · Feb 21, 2013 at 12:27 PM 0
Share

Thanks Bunny, incredibly useful! Bookmarked and will thumbs up once I find a few questions to answer and get 15 points :)

Rob

avatar image Razieln64 · Feb 15, 2014 at 02:31 AM 2
Share

Just to let you know. In Unity 4.3 it is now possible to get the serialized property type. Just use the fieldInfo.FieldType property. You don't need to pass the type in the attribute's constructor anymore. See : http://docs.unity3d.com/Documentation/ScriptReference/PropertyDrawer-fieldInfo.html

avatar image Bunny83 · Apr 22, 2014 at 06:02 PM 1
Share

@vexe: you can use and modify this helper method as you like, since it's posted here on UA. I'll keep the int + Type version since its more flexible especially in combination with a PropertyDrawer.

That way you can easily add another method like this:

 public static T DrawBit$$anonymous$$askField<T> (Rect aPosition, T aValue, GUIContent aLabel) where T : System.Enum
 {
     return (T)DrawBit$$anonymous$$askField ( aPosition, (int)aValue, aValue.GetType(), aLabel);
 }
Show more comments
avatar image
4

Answer by whydoidoit · Feb 05, 2013 at 09:10 AM

You need to use EnumMaskField and make sure your enum is decorated with the [Flags] attribute.

Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Feb 05, 2013 at 03:11 PM 2
Share

Does the Flags attribute even change anything? The implementation of Enum$$anonymous$$askField is horrible. It doesn't care about the actual enum values, it just treats the first value as 1 the second as 2 the third as 4 ...

avatar image whydoidoit · Feb 05, 2013 at 03:12 PM 0
Share

Yeah - I'm thinking of the one that I wrote and googling the wrong name :S

avatar image AlwaysSunny · Feb 07, 2013 at 06:14 AM 0
Share

Thanks so much! $$anonymous$$nowing what to research helped me learn something new. :)

avatar image yoyo · Feb 18, 2014 at 04:56 AM 2
Share

Note that applying [Flags] doesn't automatically make the enum values into powers of 2, you need to do that yourself. I like to use a shift operator, so it's clear I'm getting one bit each time, like so:

 [Flags]
 enum $$anonymous$$yEnum
 {
   First = (1 << 0),
   Second = (1 << 1),
   Third = (1 << 2),
   etc.
 }
avatar image yoyo · Feb 18, 2014 at 04:57 AM 0
Share

And yeah, it's a pity Enum$$anonymous$$askField isn't used automatically when [Flags] attribute is present :-p

Show more comments
avatar image
2

Answer by phobos2077 · Sep 08, 2018 at 05:27 PM

@AlwaysSunny @Bunny83 @whydoidoit @Aqibsadiq :

A clean solution for current versions of Unity.

EnumFlagAttributePropertyDrawer.cs

 using System;
 using UnityEngine;
 using UnityEditor;
 
 [CustomPropertyDrawer(typeof(EnumFlagAttribute))]
 class EnumFlagAttributePropertyDrawer : PropertyDrawer
 {
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         label = EditorGUI.BeginProperty(position, label, property);
 
         var oldValue = (Enum)fieldInfo.GetValue(property.serializedObject.targetObject);
         var newValue = EditorGUI.EnumFlagsField(position, label, oldValue);
         if (!newValue.Equals(oldValue))
         {
             property.intValue = (int)Convert.ChangeType(newValue, fieldInfo.FieldType);
         }
 
         EditorGUI.EndProperty();
     }
 }
 

EnumFlagAttribute.cs

 using UnityEngine;
 
 /// <summary>
 /// Display multi-select popup for Flags enum correctly.
 /// </summary>
 public class EnumFlagAttribute : PropertyAttribute
 {
 }

Usage example:

 public class MyFlagHolder : MonoBehaviour
 {
     [SerializeField, EnumFlag]
     private MyFlag flags;
 }
 
 [Flags]
 public enum MyFlag
 {
     Flag1 = 1,
     Flag2 = 2,
     Flag3 = 4,
     Flag4 = 8
 }




Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image thoroc · May 19, 2019 at 05:56 AM 0
Share

This solution doesn't seem to solve the issue where all flags option (label=Everything) return -1, whereas the solution offered by @Bunny83, however convoluted, does.

avatar image
1

Answer by Aqibsadiq · Nov 20, 2017 at 07:41 AM

@Bunny83 @AlwaysSunny @whydoidoit

Here is a simpler way

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;
 
     }




Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Nov 20, 2017 at 12:48 PM 0
Share

Well, that's not really a solution. As you might see I'm also using EditorGUI.$$anonymous$$askField in my solution. The problem is that the $$anonymous$$askField doesn't care about the actual bitmask values.


In your example your enum does not represent a bit mask at all. Your enum values are:

 public enum FurnitureType
 {
     None = 0,
     SofaPrefab = 1,
     Curtains = 2,
     Table = 3,
     Chairs = 4
 }

So looking at the bit values here your "Table" would be a Sofa combined with Curtains ^^.


You don't seem to actually be intrested in a bitflag field. I'm not sure you really understand why we use a bitmask. We can represent up to 32 individual items in a single integer value. The main point of using a bitflag is that is't way faster and doesn't require memory on the heap for managing an array of elements. We have 32 boolean values in a single integer which we can easily test if they are set or not.


The way you use the $$anonymous$$askField is the way Unity had in $$anonymous$$d, but it doesn't fit for mask enums. You basically do the same that "Enum$$anonymous$$askField" does wrong. You expect the mask value for each enum member to be in order and continous. Have a look at my "E$$anonymous$$yEnum" example. It has 4 distinct members ( Cow, Chicken, Cat, Dog ) and some combinations of those. Your solution artifically gives each enum member a different bit value which you reverse in your "ReturnSelectedElements" method. However the point was to actually have an enum that represents a bitmask.

avatar image Aqibsadiq Bunny83 · Nov 20, 2017 at 01:00 PM 0
Share

okay you are right i am not interested in bitflag field To me the Question was how to create a custom inspector multi-select enum dropdown and i just did that and its working fine

avatar image Bunny83 · Nov 20, 2017 at 01:04 PM 0
Share

Ironically Unity has an internal method called "Do$$anonymous$$askField" which takes seperate values and names arrays. This is internally used by $$anonymous$$askField. However the array with the values is automatically generated like this:

 int[] array = new int[flagNames.Length];
 for (int i = 0; i < array.Length; i++)
 {
     array[i] = 1 << i;
 }

If we had access to the internal Do$$anonymous$$askField the fix would be much easier as you just had to pass the arrays we get from "Enum.GetNames" and "Enum.GetValues" and would get the correct result.

avatar image hexagonius Bunny83 · Nov 06, 2019 at 03:49 PM 0
Share

I think that's about the 5th time that a really good feature is burried in the engine without either not being accessible or not being documented

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

24 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How to change array orders in the inspector 1 Answer

Should I use Inspector or custom editor window? 1 Answer

Using EditorGUI.FloatField With Custom Class Inspectors? 1 Answer

Custom inspector curves similar to the 'Audio Source' volume/pan/spread Curves 0 Answers

Prefix label greyed out when following component disabled 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges