Search Unity

Ability to add enum argument to button functions

Discussion in 'UGUI & TextMesh Pro' started by Slev, Sep 26, 2014.

  1. Romaleks360

    Romaleks360

    Joined:
    Sep 9, 2017
    Posts:
    72
  2. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    I'm using @llamagod 's solution, which works fine, but I'm worried about the fact that UnityEventDrawer.cs is taken from Unity source code and adds a patch in the middle of the DrawEventListener function...

    See original code on GitHub

    This code looks slighty different because:
    - llamagod is using a reverse-engineered version
    - the code has changed with the iterations for 2017, 2018, etc.
    - we must use properties and reflection to access private/internal members

    When Unity is upgraded, the original code will go further and further from our custom implementation. In the best case we'll lost some extra features and fixes from new versions, in the worst case compatibility will break and the project will stop compiling (in the editor).

    For now, I will copy-paste the patch (including reflection usage) inside the code that matches my version of Unity, but I wonder if there is not a solution involving overriding some nice method...
    Like setting a custom drawer for some IntAsEnum field, which would be used by PropertyField.

    The patching part:

    Code (CSharp):
    1.  
    2.             // Try to find Find the EnumActionAttribute
    3.             var method = GetMethod(m_DummyEvent, methodName.stringValue, listenerTarget.objectReferenceValue, GetMode(mode), desiredType);
    4.             object[] attributes = null;
    5.             if (method != null)
    6.                 attributes = method.GetCustomAttributes(typeof(EnumActionAttribute), true);
    7.             if (attributes != null && attributes.Length > 0)
    8.             {
    9.                 // Make an enum popup
    10.                 var enumType = ((EnumActionAttribute)attributes[0]).enumType;
    11.                 var value = (Enum)Enum.ToObject(enumType, argument.intValue);
    12.                 argument.intValue = Convert.ToInt32(EditorGUI.EnumPopup(argRect, value));
    13.             }
    14.             else
    15.             {
    16.                 EditorGUI.PropertyField(argRect, argument, GUIContent.none);
    17.             }
    18.  
    Also, don't forget to make the CustomPropertyDrawer work on `typeof(UnityEvent)`, not `typeof(UnityEventBase)`


    The history of my file to understand how I re-applied the patch from the released Unity code:
    https://github.com/hsandt/LD44/commits/master/Assets/Game/Scripts/Editor/UnityEventDrawer.cs
     
  3. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    @huulong Yes, your observations and concerns are valid. This is a hack. To my knowledge, this is about as good as it's going to get without being able to modify the Unity source code as an employee or overhauling the editor serialization system and inspector. I only really changed DrawEventListener, but it's a big method and it's private and it's called by another private method so it cannot be overridden without some serious reflection and low-level programming magic.

    The reason why I use typeof(UnityEventBase) is that I have to go a step up in the inheritance hierarchy such that my custom drawer will be picked for UnityEvent instead of the built-in Unity one. I can't think of any problems this could create.

    A proper fix for issues like this would require overhauling the editor serialization system and inspector. I don't know the internal workflow of Unity staff. But having attempted something like this before (also attempted by others, for example, https://assetstore.unity.com/packages/tools/utilities/odin-inspector-and-serializer-89041), I'm guessing this would take Unity 4 to 6 months for a small team dedicated to it (keep in mind this is for a brilliantly crafted solution that looks nice and is refactored many times and includes very thorough testing).
     
    Last edited: May 17, 2019
  4. fluffycritter

    fluffycritter

    Joined:
    Jun 2, 2019
    Posts:
    1
    I just want to thank you for posting this solution; I just tried it out and it works wonderfully. It'd be great if Unity were to fix it in the editor themselves but it's not all that onerous to use this workaround.
     
  5. JeevSiewnath

    JeevSiewnath

    Joined:
    Dec 31, 2013
    Posts:
    1
    Ridiculous, 2019 and still waiting for this basic functionality...
     
  6. Rathlord

    Rathlord

    Joined:
    Oct 13, 2013
    Posts:
    17
    C'mon Unity team we'd really love to have this...
     
  7. llamagod

    llamagod

    Joined:
    Sep 27, 2015
    Posts:
    76
    If you look at the Unity roadmap for 2019.3.0 "Polymorphic Serialization" is there which means they will have fixed the serialization system at that point. So I'm guessing this feature will be added relatively soon after, maybe 2020.
     
    VirtualPierogi and leni8ec like this.
  8. SerializeField

    SerializeField

    Joined:
    May 22, 2017
    Posts:
    22
    Just found out this isn't a feature. Please implement it!
     
    tarahugger likes this.
  9. tarahugger

    tarahugger

    Joined:
    Jul 18, 2014
    Posts:
    129
    You've got to be kidding me... We still can't select an enum value from a button click? :mad:
     
  10. Braza

    Braza

    Joined:
    Oct 11, 2013
    Posts:
    136
    Meh... A bump from me. Surprised it is not there.
     
  11. Hassurunous

    Hassurunous

    Joined:
    Apr 6, 2017
    Posts:
    5
    Another humble bump. I realize there are underlying complexities that make this more difficult than just "turn it on", but man it would make sense and would make my life easier.
     
  12. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    I agree. Useful feature, old thread, should be implemented. Also note that you can already select enum values for public variables from a drop-down. I would expect the same drop down to select the value for the parameter of a callback function.
     
  13. Zecrah

    Zecrah

    Joined:
    Nov 1, 2016
    Posts:
    7
    +1 for this
     
  14. justincywong12

    justincywong12

    Joined:
    Feb 15, 2019
    Posts:
    2
    Hey this is a workaround that I've found works. It has a fail-safe incase you make an error in your string input

    if( System.Enum.TryParse<YourEnumType>(yourString, out YourEnumType yourEnum)
    {
    //use yourEnum to set your desired enum to the string input
    }
    else
    {
    Debug.LogWarning("Error in enum conversion");
    }
     
  15. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    The workaround I use is to have a Holder class, which only has a public field to set the enum value. You then create one instance of this class for every value of the enum (collect these instances in a prefab so you can reuse all values across different scenes). Afterwards you write your callback such that it accepts an instance of the holder class instead of the enum.

    Example:
    Code (CSharp):
    1. public class SceneEnumHolder : MonoBehaviour
    2. {
    3.     public EScene scene;
    4. }
    Code (CSharp):
    1. public void OnButtonClicked(SceneEnumHolder holder)
    2. {
    3.     Debug.Log("you are in scene "+holder.scene);
    4. }
     
  16. PitaBreadGames

    PitaBreadGames

    Joined:
    Sep 15, 2017
    Posts:
    5
    Humble Bump
     
  17. alternative_richie

    alternative_richie

    Joined:
    Sep 4, 2014
    Posts:
    15
    @llamagod Thanks for your solution! Nice....
     
  18. ponos_sanchez

    ponos_sanchez

    Joined:
    Aug 16, 2017
    Posts:
    2
    This need implement ASAP in the editor. It's a basic function.
    Any progress Unity team?
     
  19. JohnHudeski

    JohnHudeski

    Joined:
    Nov 18, 2017
    Posts:
    126
    Are you kidding me?
    5 Years later
     
  20. alexsyo

    alexsyo

    Joined:
    May 1, 2016
    Posts:
    1
    Is there any progress on the implementation from the Unity team?
    .. or better any progress on taking this into consideration...
     
  21. Sparkline

    Sparkline

    Joined:
    Feb 8, 2013
    Posts:
    121
    5 years ago I had to froze my project until this feature will come out. Since that time I became father and got a child (son 3.5 years old now). Just checked this thread, still no implementation... uh...ok!
     
    Hades714 and neilsarkar like this.
  22. gino_pisello

    gino_pisello

    Joined:
    Jun 24, 2013
    Posts:
    33
    I've not been here since 2014, but I need this feature too
     
  23. wpetillo

    wpetillo

    Joined:
    May 23, 2017
    Posts:
    24
    +1 because this would be a nice feature to have for buttons, +10 because the same issue applies to UnityEvents more generally (which are one of Unity's best and underused features), +100 because it reflects an underlying technical debt issue in the engine.
     
    Cogniad and leni8ec like this.
  24. haywirephoenix

    haywirephoenix

    Joined:
    May 17, 2017
    Posts:
    109
  25. rsodre

    rsodre

    Joined:
    May 9, 2012
    Posts:
    229
    I took it for granted, was puzzled of why methods with enum parameters were not showing in event lists.
    Then I found this thread...
    :eek::eek::eek:
     
  26. unity_3luka1

    unity_3luka1

    Joined:
    Feb 3, 2020
    Posts:
    1
    Are you kidding me Unity? What is this crap
     
  27. Planet11

    Planet11

    Joined:
    Nov 3, 2011
    Posts:
    23
    llamagod, I just tried using your workaround in 2019.3.0f3

    I'm getting a null reference in UnityEventDrawer at the line:
    var type = Type.GetType(prop.FindPropertyRelative("m_TypeName").stringValue, false);
    and no events at all are appearing in the inspector on any buttons. (toggles are fine)

    Reverting it now, I'm not sure, but I think most likely it is because I tried using it on a toggle, hoping to see an enum in an OnValueChanged Event rather than a button's OnClick, anyway just thought I'd give you a heads up that I managed to break it.

    Thanks very much for the workaround by the way, it's appreciated.

    In case you're wondering why anyone would need an enum on a toggle, they're a group of category filters so I can toggle all the weapons, or all the engines etc.
     
  28. haywirephoenix

    haywirephoenix

    Joined:
    May 17, 2017
    Posts:
    109
    Guys, the workaround didn't work for me either

    but this works, try it!

    https://assetstore.unity.com/packag...10.681738839.1581068424-1078625206.1576573019
     
    Last edited: Feb 12, 2020
  29. Planet11

    Planet11

    Joined:
    Nov 3, 2011
    Posts:
    23
    I couldn't get the workaround going after removing the toggles, could be that it's broken in 2019.3?
    I ended up just ditching the button component and writing a tiny replacement button class that takes a specific enum from an unrelated class and hands it along, so it's not reusable but it does the job.
     
  30. Simon-O

    Simon-O

    Joined:
    Jan 22, 2014
    Posts:
    51
    Five and a half years, and still no progress.
     
  31. mbshaikh19

    mbshaikh19

    Joined:
    Mar 27, 2017
    Posts:
    2
    At least now I know, its not possible in Unity.
     
  32. haywirephoenix

    haywirephoenix

    Joined:
    May 17, 2017
    Posts:
    109
    https://assetstore.unity.com/packag...10.681738839.1581068424-1078625206.1576573019
     
  33. neilsarkar

    neilsarkar

    Joined:
    Aug 11, 2017
    Posts:
    11
    +1 would use this feature
     
  34. renanbp

    renanbp

    Joined:
    Dec 14, 2017
    Posts:
    1
    They can put a man on the moon and nest prefabs but enums on button click parameters is still impossible!
     
    Shizola and tarahugger like this.
  35. laessnb

    laessnb

    Joined:
    Jun 10, 2014
    Posts:
    101
    +1 to hoping for this major QoL feature. I keep forgetting it doesn't exist, wonder why my methods don't show up in the button dropdown, remember it and smack myself and/or bystanders.
     
    Daerst likes this.
  36. Daerst

    Daerst

    Joined:
    Jun 16, 2016
    Posts:
    275
    Smacking bystanders is a superb solution to whatever problem at hand.
     
    laessnb likes this.
  37. GarrettCMiller

    GarrettCMiller

    Joined:
    Jan 20, 2013
    Posts:
    30
    Not gonna lie, I love Unity (a lot), but the fact that 5-6+ years later this whole "enum as a parameter" still isn't possible is getting a bit tiring. Please Unity devs, outside extenuating circumstances, this should be fixable in less than a few hours and would be super helpful! Thanks!
     
    Hades714, laessnb and tarahugger like this.
  38. tessiof

    tessiof

    Joined:
    Dec 6, 2017
    Posts:
    25
    I managed to overcome this limitation by using a scriptable object instead of an enum.




     
    urun4m0r1 likes this.
  39. bmoodyCM

    bmoodyCM

    Joined:
    Mar 23, 2020
    Posts:
    1
    SMH - come on Unity!

    Does anyone know of an asset store package that can do this quickly?
     
  40. ianpea

    ianpea

    Joined:
    Dec 14, 2018
    Posts:
    1
    Do you mind sharing the script of the scriptable object? How would you call the "ShopItem" scriptable object in Unity, let's say when you wanna use it in code?
     
  41. TomPo

    TomPo

    Joined:
    Nov 30, 2013
    Posts:
    86
    Unity team - stop working on some advanced stuff that will be used by 1% of people when you still didn't implement such basics solutions which would be used by 99.99% of people.
    Pass enums via buttons is one of these solutions.
    This is just ridiculous in 2020.
     
  42. wpetillo

    wpetillo

    Joined:
    May 23, 2017
    Posts:
    24
    Any class inheriting from ScriptableObject can be passed as a static argument in a UnityEvent. Until Unity gets its act together and starts fully supporting its core features, this is currently the best way to generalize UnityEvents (which are the basis of the best way to write modular, extensible code). For those less familiar with ScriptableObjects, here is a quick summary of how to use them in this way:

    Create a class like this:

    Code (CSharp):
    1. [CreateAssetMenu(menuName = "You File Path/With Subfolders/Here", fileName "Default File Name")]
    2. public class YourClassName : ScriptableObject
    3. {
    4.     public VariableType1 value
    5. }
    You can put any data you want inside the ScriptableObject, including enums, references to prefabs or other ScriptableObjects, primitive data types, whatever. To use that data in a class that wants, for example, a Vector3, write methods like the following:

    Code (CSharp):
    1. // Example using a Vector3: create an overload with a ScriptableObject, extract the desired data from it and pass it along to your original version.
    2. public YourMethod(YourScriptableObject data)
    3. {
    4.     YourMethod(data.value);
    5. }
    6.  
    7. // Your code that does stuff here
    8. public YourMethod(Vector3 direction)
    9. {
    10.     ...
    11. }
    12.  
    13. // Example with enum:
    14. public YourMethod2(YourScriptableObject2 data)
    15. {
    16.     YourMethod2(data.value);
    17. }
    18.  
    19. public YourMethod2(YourEnum type)
    20. {
    21.     ...
    22. }
    Note that ScriptableObjects can hold an open-ended amount of data values in them, of any type, including references to prefabs, other ScriptableObjects, and so on. They can also contain methods, but since ScriptableObjects are resources, using methods in them should be thought of like using static methods, but with better encapsulation.

    In comparison to passing enums via UnityEvents, passing ScriptableObjects is the more robust solution (can handle a wider range of use-cases), but on the other hand is a bit more cumbersome to set up (both in terms of extra code, and for designers to use). So there shouldn't be any cases where you are totally stuck...it's just annoying to have go to extra steps to do things this way for simple use-cases where there should be a trivially simple alternative.
     
  43. tessiof

    tessiof

    Joined:
    Dec 6, 2017
    Posts:
    25
    Sure.

    The base class:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ShopItem : ScriptableObject
    4. {
    5.     public int value;
    6.  
    7.     public virtual void Equip() { }
    8. }
    An item:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [CreateAssetMenu (menuName = "Shop Items/KeyToCastle")]
    4. public class KeyToCastle : ShopItem
    5. {
    6.     public override void Equip()
    7.     {
    8.         GameManager.instance.HasKeyToCastle = true;
    9.     }
    10. }
    The shop class:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Shop : MonoBehaviour
    4. {
    5.     private ShopItem itemSelected;
    6.  
    7.     public void SelectItem(ShopItem shopItem)
    8.     {
    9.         itemSelected = shopItem;
    10.     }
    11.  
    12.     public void Buy()
    13.     {
    14.                    // Check if Player can actually buy the item...
    15.  
    16.                    // If she can, equip it
    17.                    itemSelected.Equip();
    18.     }
    19. }
    20.  
    Bonus tip: https://unity.com/how-to/architect-game-code-scriptable-objects
     
  44. Immortal_Squad

    Immortal_Squad

    Joined:
    Apr 9, 2020
    Posts:
    1
    Hurry its just 6 years and not a word from the unity team. Congratulation on creating HDRP but no Enum support in the inspector.

    Shame full scene for unity team...
     
  45. lightbringa

    lightbringa

    Joined:
    Nov 17, 2017
    Posts:
    1
    Hello everyone,

    since they seem to take their sweet time with this I've managed to create a solution which works quite nice for me. I uploaded the source code as Thumbnail if someone is interested.

    In short: I look for every GameObject with a Button component, display the results with the help of a custom inspector and add an event to the OnClick function of the found buttons.

    For everyone unfamiliar with custom inspectors (Brackeys tutorial):


    1. A Screenshot of the extension in action in my current Project Inspector.png

    2. Class EnumButtonHelper.cs.
    This class sits on the topmost UI Canvas since only children are considered relevant.
    Code (CSharp):
    1. private Action<UIButton> buttonMethod;
    2.     private void Start() {
    3.         //buttonMethod = GetComponent<UIHandler>().PressUIButton;
    4.         buttonMethod = ButtonTest;
    5.         _setOnClick();
    6.     }
    The buttonMethod variable is a only to simplyfy changing the OnClickMethod used, this can be removed if you don't like the use of Action.

    3. Class EnumButtonHelperInspector.cs.
    Needed to call the ButtonHelper "RescanForMissingButtons" function and to get rid of the standard array view. In theory the script would work without it but I don't like pulling every single button from the Hierarchy to the array, so I highly recommend using the inspector as well.

    Edit: If you use buttons the normal way as well, it might be usefull to extend the _setOnClick method to skip some entries.
    Code (CSharp):
    1.   private void _setOnClick() {
    2.         if (buttonInfos == null) return;
    3.         for (int i = 0; i < buttonInfos.Length; i++) {
    4.             if (buttonInfos[i].buttonEnum == UIButton.unknown) continue;
    5.             buttonInfos[i].button.onClick.RemoveAllListeners();
    6.             int index = i;
    7.             buttonInfos[i].button.onClick.AddListener(() => {
    8.                 buttonMethod(buttonInfos[index].buttonEnum);
    9.             });
    10.         }
    11.     }
     

    Attached Files:

    Last edited: Jun 2, 2020
  46. jeppe79

    jeppe79

    Joined:
    Sep 17, 2019
    Posts:
    76
    I went googling for this feature and found this 3 page long thread from 2014 and thought "Sweet! This must have the solution"... then I saw a Unity official saying they will add this feature... "SWEET! Then it MUST exist by now!"...
    Kept reading and reading... years going by... arrive at the last post and still nothing.

    I am very sad now.
     
  47. TomPo

    TomPo

    Joined:
    Nov 30, 2013
    Posts:
    86
    Yeah... Unity team forgot about 80/20 rule.
    Now their 99% of efforts are put into solutions which would be used by 1% of people.
    But solutions used by 99% of people are totally ignored FOR YEARS!

    - multiplayer - gone. They will introduce some new ones... soon... maybe. Maybe this time, not 'user as host' based.
    - cloth system - still can only collide with two stupid colliders, terrain or capsule not included
    - can't pass enums via buttons
    - can't see Dictionaries inside the inspector
    - navAgents going stupid when you have over 100 of them as SetDestination is async but calculate path isn't so they are just waiting and doing nothing
    - Mono is still not serializable and/or there is no built-in save system as is in other engines based on some internal database system (but hay, you can use Registry to save something - sic!)
    - decals are material-based (you have to turn on/off receiving them in a material) instead of layer-based
    - the newer is solution - the worse the documentation is

    I can continue with this list for another dozen points.

    Buy hay - we will have ML-Agents - yeah!

    Spending 99% of efforts for things being used by 1% of people - it's just 99% wasting resources.

    QUO VADIS UNITY ?!
     
    Darkziss, normus28, JoRangers and 2 others like this.
  48. DracoDevStudent

    DracoDevStudent

    Joined:
    May 7, 2020
    Posts:
    1
    It would be a really nice feature to have!

    Bump from me too!
     
  49. darkmask58

    darkmask58

    Joined:
    May 9, 2015
    Posts:
    25
  50. Valera981

    Valera981

    Joined:
    Apr 22, 2014
    Posts:
    12
    +1 sad too