More serialization-friendly alternative to enums?

I have a pain point around using enums when it comes to editor serialization, and I wanted to see what kind of alternatives/solutions other people use to work around this.

The problem can best be illustrated with an example:
In a theoretical game, a character may have some upgrades represented by an enum:

enum Upgrade {
  WallJump,
  KungFu
}

Over time, I’ll both reference this in code, as well as as have serialized fields of this enum type through the inspector, which Unity will store as an integer.

One day, I decide I want to add a new Upgrade: DoubleJump. Since it relates to jumping, I place it after WallJump in the enum definition:

enum Upgrade {
  WallJump,
  DoubleJump,
  KungFu
}

But something’s wrong! Now, every enum value I had serialized of KungFu is now this new DoubleJump upgrade. Whereas a serialized int value of 1 previously meant KungFu, it now means DoubleJump. To fix it, I’d have to go through all of these locations and set the enum property to KungFu again so it’s serialized to the new value of 2.

How can I define something like an enum without needing to manually readjust all of the subsequent serialized values every time something is inserted?

I’ve thought through some options to , but I don’t particularly like any of them:

  • Make it a rule to never insert into the enum. This makes things a bit unorganized, and removes the ability to leverage enum order

  • Assign values to enums that are distant enough that new values can be assigned between them. This is a bit of a hassle, and removes the ability to do something like a COUNT option at the end of the enum

  • Use strings ids instead of enums. This would likely require a custom property drawer to show up nicely in the inspector, and even then it wouldn’t allow for renaming without manual readjustment

  • Use scriptable objects instead of enums. While this is nice for the inspector, it’s a bit of a hassle to code around. Plus you wouldn’t have any implicit order or count available. Also it’d be a pain to sync over multiplayer.

Are there any other options or best practices I haven’t thought of?

1 Like

It’s possible to assign a unique int for each, i.e.,
WallJump = 0,
KungFu = 1

Then add whatever new ones sorted however you like,
WallJump = 0,
DoubleJump = 2,
KungFu = 1

…if you don’t assign a value, it automatically goes 0, 1, 2 etc., that’s why adding a new one in between messes things up.

2 Likes

Your solution #4 is the solution I use. I personally never use enums in situations where there is even a slight chance to have to add/remove options, unless that enum is entirely internal to one class.

Pretty much the rest of the time I use Scriptable objects, particularly because they can then contain the data/logic internally rather than running through a list of enum options in a case statement.

They do require you to change how you code. In this example, they would contain the logic of what the upgrade does themselves, but if anything this feels like better architecture to me in the long run.

1 Like

Huh, I didn’t think of using unordered enum values like that. That makes things a lot nicer, though there’s still the annoyance of losing enum order if you want that for iteration or anything. Very nice approach if you don’t need that though.

The capability of adding extra data is definitely a nice selling point I didn’t consider (I was mostly just thinking of them because they’re easy to select in the inspector). That’s a nice enough bonus that I think I’ll want to experiment down that route, even if it means changing the way I code a bit.

Thanks for the replies!

1 Like

Enums can also be used to call functions via delegates without any if/else or switch, for example…

  public delegate void UpgradeHandler();
  public UpgradeHandler[] upgrades = new UpgradeHandler[System.Enum.GetValues(typeof(Upgrade)).Length];
  public void Awake()
  {
    upgrades[(int)Upgrade.WallJump] = UpgradeWallJump;
    upgrades[(int)Upgrade.KungFu] = UpgradeKungFu;
  }
  public void UpgradeWallJump()
  {
    Debug.Log("wall jump upgraded");
  }
  public void UpgradeKungFu()
  {
    Debug.Log("kung fu upgraded");
  }
  public void PerformUpgrade(Upgrade upgrade)
  {
    upgrades[(int)upgrade].Invoke();
    // ..or make a small change so it only calls the function if one has been assigned:
    // upgrades[(int)upgrade]?.Invoke();
  }

Then anywhere you want, you can…

PerformUpgrade(Upgrade.KungFu);

Edit: On further thought, note the above would only work with normal enums / not if setting specific values out of order :frowning:
Edit: Also it’s probably better performance unless you have a vast quantity of values just to iterate using if/else or switch… I don’t know how the compiler works well enough to say for sure what kind of overhead there is. I often cast enum to int for grabbing a specific value from an array based on the enum, i.e. myarray[(int)Whatever.Thing], but haven’t tried the above approach for calling functions (just thought of doing it to avoid the iteration… but less interested now that I’ve though of it more).