Unity Devs: Enums are your worst enemy! Here’s what you should use instead
If you know at least a thing or two about Unity, you may have heard about this pretty cool thing called “Serialization”. It lets us have neat features like Inspector-assigned variables:
Developers generally don’t want game designers having to mess with their code to change gameplay variables, so exposing values to the inspector is a handy way to avoid that.
A common trend is using enums to display a dropdown menu with a list of possibilities. Let’s say we want to define Spell types:
As we add new features to our game, we may want to create new spell types. We do that by inserting into our enum:
However, we already have a problem. Any GameObjects which we previously assigned Ice will now be Wildfire. Objects that were assigned Water are now Blizzard. Why is that?
Enums have implicitly defined values.
When you define an Enum, values are implicitly assigned to all elements unless you explicitly assign them. By default:
As enumerations are valued types, Unity serializes them using their integer value. When previously Ice = 1 and Water = 3, now they are Ice = 2 and Water = 6. This exposes our first problem when using Enums:
When you add a new entry, references are lost.
Imagine that you have a game with dozens of spells, carefully grouped by elemental type in their enumerations so that the designer can easily find them in the inspector. Hundreds of GameObjects and Prefabs are serializing your enumeration.
What if you need to add a single Inferno spell to the fire element? Of course, you can gimmick your way out with some empty space:
Now we can create 16 spells per element type without losing references in our GameObjects and Prefabs. And of course, we can increase this.
But let’s consider for a moment the following possibilities:
- Someone in your team assigns a spell to the incorrect category; They save the project and commit.
- You return to the project after a long time off and make the same mistake above.
- Your enumeration becomes gigantic, to the point of not being able to select a spell within your screen.
These situations are incredibly harmful for the scalability and maintenance of your project. Therefore, we must identify the underlying problem of enums:
You have to know all the possibilities beforehand.
Enums may be useful for domains with finite possibilities, such as days of a month or months of a year. However, this is not the nature of how games are made. Most likely we don’t know our scope when first implementing a feature.
So what’s our solution?
ScriptableObjects!
You may have heard of them quite a lot while studying Unity. And that’s for a good reason: they go hand-in-hand with the concept of Serialization, making them perfect for inspector assignment.
Here’s how:
Wait, that’s it?
Yeah, pretty much. We don’t even need code in our ScriptableObject.
Now we can add new spell types through the Project view:
And assign them through the inspector:
Did you notice that we now have a fully functional search bar for all our Spell types? Also, we can organize them in folders in our project.
Pretty neat, huh?
So how do we use this in our game?
Let’s create an enemy which has weakness to certain spell types. We can serialize a List of Spell Types:
Then, when the enemy gets hit by a spell, we can compare the spell type by reference:
This functionally is the same as having an enum, while avoiding all the issues we discussed earlier. We also have the added benefit of having an enhanced inspector!
You could also attach data to the spell types, but that’s a topic for another post…
This solution is closely related to the Type Object pattern. If you would like to know more, read this awesome article.
Also, if you would like to know about more possibilities with ScriptableObjects, check out this amazing talk.
Have fun and keep coding!