Enums are kicking my arse, is there some superior solution I'm not aware of?

So enums… they have been the bane of my existence for longer than I care to admit. I structured my bonus system around them and I plan to have TONS of bonuses so it’s important the list be alphabetical.

I’ve been too stubborn to make a post about them over the past couple years as they seem like they should be so simple, but they continue to just ruin my day over and over again and now at this point I don’t even want to think about how much time and motivation I have lost dealing with this forsaken things! So I’m finally breaking down and making this post.

If I create enums like so:

public enum zxBonus
{
    None,
    Anger,
    AfterShocks,
    Behemoth,
    Berserker,
    BlackJack,
    Block,

It’s simple, but at any point I rearrange or add to the list all existing references get garbled.

public enum Bonus
{

    Behemoth = 3,
    None = 0,
    Anger = 1,
    AfterShocks = 2,
    BlackJack = 5,
    Berserker = 4,
}

If I set it up like this, it becomes a messy nightmare and things get out of alphabetical order and once I have a decently sized list it’s a pain. It just feels wrong.

I’ve gone so far as to create custom classes that store a secondary string value and then accesses the enum from that string:

[Serializable]
public class SafeBonusReference
{
    public string enumString = "None";
  [ShowInInspector] public Bonus bonus { get { return DataManager.GetBonusEnum(enumString); } set { enumString = value.ToString(); } }
}

public static Bonus GetBonusEnum(string name)
{
    foreach (var enumName in Enum.GetNames(typeof(Bonus)))
    {
        Bonus checkedEnum = (Bonus)Enum.Parse(typeof(Bonus), enumName);
        if (enumName == name) return checkedEnum;
    }
    print("enum Not Found for " + name);
    return Bonus.None;
}

Am I just doing this totally wrong? I’m relatively new to code and it just totally blows my mind how clunky and terrible Enums are. There has to be a better solution for creating a list with names on it right? I have no idea, the more I learn about gamedev the more I see just absolutely lacking key functinality that just sits there and everyone just deals with it.

But i’d be happy to learn that there’s some super simple solution I’m just not aware of and that I’m a moron!

Another thing I’d love to do with enums but they’re just such a pain is to generate list type options elsewhere. There are so many times i’d love to have a 3rd or 4th option, but bools only allow “true/false” and if I resort to something like an int for options 1,2,3,4, etc. They don’t communicate any sort of inference of what they do. I guess I could use a string condition, but that’s prone to human error and who wan’ts to constantly deal with quotation makes?

Hope I’m properly communicating all these problems, I’m burned out from dealing with these things all day for the 50th time and can’t even think straight.

Bonus question… I have several classes that I’d love if they just automatically returned a specific type of value. So in the above class if I want to get the bonus value returned I have to type
“safeBonusReference.bonus”

Is there any way to get this class to return the Bonus by default by just typing “safeBonusReference”

I present to you the amazing Dictionary:

Sadly Unity never implemented a serializable editor for it. I had to make my own.

2 Likes

Does that mean that if I used dictionaries I wouldn’t get inspector dropdowns like this for things like prefab inheretance? I’m not the savviest coder around.

image

You are goddam right.

You will need to implement your own inspector. May the power of LLMs help us.

1 Like

Rofl. I always find it funny when I ask you guys who actually know what you’re doing what the sane solution is, only to find out there isn’t one.

Like WTF? You would think having a list with options in it that can be kept organized would be so key to game development!

I dunno, I still feel like I’m an idiot and i’m doing this totally wrong. There has to be a decent solution to this core problem… Right?

Like Holy crap I’ve been working on my project for years, and after all this time I FINALLY got the courage to try an initialization solution that would allow me to keep my enum in alphabetical order in the enum, but garble it elsewhere, but then I realized all my in game assets that reference the list get totally garbled any time I add new entries into it.

So now i’m down the rabbit hole of creating this crazy new class types and invalidating all my data AGAIN for like t he 50th time.

I’m to the point where I wish I’d just used damned indexes in an arbitrary list of bonuses. It would be a pain in the ass, but at least I wouldn’t be constantly destroying my data.

1 Like

The Unity way be like:

Right, but i’m not trying to go back in time, I’m literally just trying to make a LIST in alphabetical order that isn’t prone to destroying all existing data.

It’s F*(King 2024, why is this such a nightmare, lol.

I feel like i’m taking crazy pills. 99% of what Unity does feels like magic, and then when it comes to creating lists of things that I sometimes add to or rename, I feel like reality is unraveling and trying to make me go insane.

I’m just trying to do my damned job here and make a bonus system that doens’t break if the wind blows the wrong way

Edit: Quick followup rant. I’m so tired of losing data for changing a variable name, or migrating a float to a new class type for reasons like the one detailed above. It would be so nice if we had some means of easily redirecting old metadata to a newly created variable type. Fat chance I know…

I really feel like we need to reevaluate top to bottom how we go about storing data in a manner that’s friggin’ sane for gamedev. It’s totally ridiculous how terrible this process is.

Making lists, storing data, making it not prone to being lost as we rename things or change variable types. Making it easily accessible in the editor.

Blah. Tech is so weird. It simultaneously blows my mind how amazing it is, and also how downright horrible it is so often.

You do not understand enums (it seems) and so you call them and Unity stupid. Consider that I have an AppTextId enum with 200 or so values. They represent word identifiers so game objects can be assigned text values indirectly. They are ordered and things don’t break when I add a new one which I do every time I add another unique text string.

Maybe take a breath and think “how is it professionals have used these things in multiple languages for decades”. Then ask a simple question and skip the “crazy pills” stuff. It makes me at least not want to bother to help.

I’ve tried twice now and I can’t decipher what you want to accomplish, particularly with the introduction of SafeBonusReference and GetBonusEnum methods.

If you assign a property an enum type it has to be assigned an actual enum value, at least that is how it works in my code. I use them all over the place.

No shit? That’s why I made this post.

If you’d like to help me understand them better, I’m all ears, I am actively seeking answers to these questions.

Quick Video showing this issue in realtime:

You neckbearding out how fantastic you are with this system in no way has helped anyone better understand the enum workflow.

Enums don’t exist for the compiler, they are just integers. They are swapped for integers during compilation and for that reason combined with Unity’s serialization they are not suited for anything that is going to change often. They are more suited for things that don’t change and are ordered like the days of the week or for things that order doesn’t matter.

I haven’t understood exactly what you are trying to solve here and you need your bonuses ordered, but to me it seems you are mixing game design and programming. You need your enum ordered because it is easy from a game design point of view but enums are hard to maintain in code. In games that are that big, with so many options like you have shown in your video, data is not hardcoded. Designers use their own programs to produce a file and your code reads from that file, without caring about the values.

For example if you have chests that unlock after a certain lockpick percentage, that percentage is not hardcoded into the code. Instead you will have easy, medium and hard chests plus the level. Your code will read the number from a file, for example for easy chest in level 2 will be 20% lockpick. That way any change can happen without changing the code, recompiling and rebuilding the game.

In your case, you have to find a way to represent those bonuses in a file, that you can edit outside Unity, not in an enum that will be chosen in the Unity editor. This file will contain a table that your code will read and get the appropriate bonus. Find an external tool that will be convenient to you to describe what bonus happens when? where? from Whom? its value etc. and then save that file and read it from code. It can be as simple as a text editor, a csv file an xml etc.

If I understand correctly, you need something like an enum, but where you can choose multiple values? For example if you have the colors Red, Blue, Black, Brown to be able to choose not only one, but any combination, like the value Red and Brown?

This can be done by overloading the explicit and implicit operators. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/user-defined-conversion-operators If your conversion does not loose any precision and it cannot fail then you overload the implicit operator, otherwise the explicit.

1 Like

Bunch of weird responses in this thread. Not sure why no one mentioned scriptable objects yet?

Enums are an anti-pattern. They do not contain any information or functionality on their own. The correct object oriented approach is to make objects out of these bonuses, so they can both contain their particular implementations and data.

Most often in Unity this is done with scriptable objects, as they’re easy to implement, natively supported, and can even allow for polymorphism (you can reference derived types in a base type field).

If you ever feel the need to reach for an enum, you shouldn’t 99% of the time.

3 Likes

I’m guessing this is in your response template… consider removing it.

While not TECHNICALLY the solution I was hoping to get, this post is so helpful that I’m calling it a solution anyhow.

I still think it’s crazy that we don’t have a reliable and expandable design asset directly in Unity :stuck_out_tongue:

I guess it’s high time I look into parsing external data and begin looking into solutions, but to be totally honest, by far the best solution would just be something IN UNITY that i can edit after a bit of ad hoc play where i just quickly adjust the asset. in the scene in a matter of seconds.

Now that i’m thinking about this more, the more I want to try to come up with a solution that lives in the engine, but isn’t subject to overides and resets from serialization.

This is just so core to the well being of a game i’m still kinda irked that there isn’t an out of the box immediate and mass adopted solution to the problem of maintaining and referencing external variables.

Saw this post on Twitter a bit ago and it looked like an awesome solution, but also one that would take a lot of setup, and I wish I had the time to pursue this, but I just really want to work on my project and not devote days to picking up excel and learning how to interface with it:

Oh well, was hoping to have a clear gameplan moving forward after finding out my options but more confused than ever by the best means of logging data that wont be blown away and is easily accessed.

There are a million and one other things I wish i could improve about my project, but i’m just so damned spread thin right now.

Thanks for the replies and insights everyone

I realize my post was insufferable, sometimes I try to fight fire with fire.

Funny you mention SO’s the majority of where my ability data is housed is in SO’s (you can see this in the video).

I’m able to avoid this issue on the unique per bonus SO’s because at runtime in awake after hitting play the SO’s share the exact name of the bonus enum string and it does the lookup there and reinitializes the bonus enum based on the string.

Where this becomes a problem is on items the player equips in my game and elsewher, where an asset may have several bonuses on it not pertaining to the namesake of the bonus and I rely on the enum itself. Which is why i’m using these hacky solutions that instantiate the string rather than the index when setting the value.

EDIT:
Oh I should mention why it’s important my data is set up this way and why i don’t just swap in an SO per bonuses where they are needed.

My bonus system is a giant list, so no where in my project do I have a variable “healthBonus” that’s explicitly called where I can plug in said SO, I reference them from this large dynamic list:


    [HideInInspector] public Bonus berserker { get { return bonusList[(int)zxBonus.Berserker]; } set { bonusList[(int)zxBonus.Berserker] = value; } }

I realize this is super confusing. Sorry about that, I don’t realy get it either, and it’s entirely possible I set this all up in the most stupid way possible. The reason I set it up like this was so I could have a really easy to set up system with the smallest amount of code required to access this data and setup per bonus I add to the game. Honestly my brain is so fried right now that I can’t even articulate why I did it this way, but i’m PRETTY sure I had a good reason.

If I remember correctly I didn’t want to have to manage these references by hand and wanted to actually cut down on the # of errors that could happen with references, so the whole system is a dynamic list that relies on the index of the bonus generated by the string name.

EDIT: I REMBER NOW! By setting it up like this i’m able to have external assets very easily reference this list with an enum dropdown menu to adding the proper bonuses. If I didn’t set it up this way my code would be vastly more complex as I would need to create a means of referencing each individual variable on the player per bonus. So if a shield improved the “bersker” bonus, I would need some logic per bonus that allowed me to reference those bits of data and modify them for “berserker”. But because i’m using a dynamic list it’s much easier to reference and level up these bonuses globally.

    public void AddBonusLvl(zxBonus bonusEnum, int levelsToAdd = 1, bool runBonusInitlialization = true, bool itemLevels = false)
    {
        if (!itemLevels) bonusesPlayer[(int)bonusEnum].permanentLevels += levelsToAdd;
        else bonusesPlayer[(int)bonusEnum].equipmentLvls += levelsToAdd;

        if (runBonusInitlialization) SetBonuses(); //Prevents debugs from lagging when adding tons of bonuses at once
        var bonus = bonusesPlayer[(int)bonusEnum];

        if (Time.time < .1f || itemLevels) return; //Prevents debug spam
        var texts = gamehud.bonusTooltipLvlUp.GetComponent<BonusTooltip>();
        if (texts)
        {
            texts.description.text = bonus.revampString;
            texts.upgradeImage.sprite = bonus.bonusImage;
            texts.description.fontSize = gamehud.rescaleFontSize(texts.description);
        }

        var uiDuration = 7f; if (bonus.bonusUiFired) uiDuration = 2f; //shorter after recieving once
        bonus.bonusUiFired = true;
        gamehud.BonusOnLvlTargetOpacity = uiDuration;
    }

If I’d known what a nightmare enums were at the time I woulda set up an entirely different system, but everything is 2020 in hindsight. That said, I can’t think of any built in solution other than enums that would solve this problem. I just need a list of “things” that I can properly adjust over time that doesn’t reorder itself and is easy to access. I think about maybe making custom lists with custom class assets that pairs an index # with a string for its name, but that starts getting messy in all sorts of new ways.

I guess the best solutin would be to parse a text file with the bonuses and their index #, but that just raises all new problems as I would have to go in by hand and shift those index numbers any time I insert a new bonus alphabetically into that list.

You should still dump the enums. Your code base and project will suffer the more you rely on them. Scriptable Objects should be the general approach for a light object oriented, but primarily data driven approach; an approach that works well in Unity.

And then on the more advanced level there is [SerializeReference] which lets you serialize regular C# object with polymorphism, cutting down on the need for tons of scriptable object assets. It requires custom inspector support, but as you’re using Odin you already have that out of the box.

1 Like

I apprciate the continued insights, but this is all over my head. I’m just trying to punch data into my project and burned out and the thought of diving into other coding paradims while I juggle the entirity of my project is too much for me right now.

I’m still glad I made this thread though as I was curious if there was some obvious solution I was overlooking, but that doesn’t seem to be the case.

If you’re doing things the stupid way, it’s nice to know that the stupid way isn’t AS stupid as you feared it was.

Really quick for reference this is what an item in my project looks like, this is a spiked shield that has 2 bonuses i need to apply to the player, and I need to use the above custom class workaround to ensuree that the data isn’t lost when the enum list is updated with new values that often shift the order:

And the general best way to author data in your projects is scriptable objects. That’s what they’re designed for, and it’s really the bottom line of what I’m saying. Swap enums for scriptable objects; that’s it.

To be honest I’m not sure why you ask for help when you either attack responses or say it’s too hard. the issue here isn’t Unity, it’s your attitude. Your time at this point would be better spent putting aside your project and doing some proper learning of C# and Unity.