Multi-Type Monsters! A Look at Enums with Flags Attribute.
Recently as a challenge I tried to create a Pokémon battle system relying heavily on scriptable objects. I had a general idea of how everything worked and I figured it would be a fun deep dive. An interesting wall I hit was when it came to monster types. My first instinct was to create something like this:
With Type serialized you get a nice dropdown window:
We could then run checks on this enum using something like:
The roadblock I ran into was that out of the box enums hold one value. They technically store an integer value, and in reality they look like this:
They auto assign an integer value starting at 0. We can also custom assign a value like I did with NEWTYPE. This makes comparisons really fast as we are just comparing two integers, and it makes it easy to read and remember what values they represent. So in code when we compare to see if the other monster is FIRE type we are really just checking if 0 == 0. This works great, except for one oversight. Multi-typing. My monster’s type cant be FIRE and GRASS at the same time, because the enum only holds one value. The type cant be equal to 0 and 2 at the same time! My first initial thought was just to create a Type1 and Type 2, but that starts getting messy down the line with type comparisons, and now you have to have a type called none just incase it isn't a dual type. And what happens if mid development you decide you want to add a monster with 3 types? You would have do go through and change all the methods handling comparisons.
So what is the solution? The enum flags attribute. By adding the attribute [Flags] in front of our enum, our values change from the screen shot above, to this:
A couple things to note here. In order for this flag system to work correctly, we need to manually assign the values in powers of two. Also we in order to get the [Flags] attribute we need to be using the System namespace. So what exactly are we looking at here? We are now treating these values as bit masks rather integer values. The comments on the right are their values converted into binary. There is a nice rabbit hole you can fall down if you want to dig into how all of it works, but we are going to focus on how to use this for our project. When implemented as above, our regular dropdown list changes into what unity calls a mask element.
Notice how we now have a field for “Nothing” and “Everything”. If you have dealt with masks before in unity this will look very familiar, because its using the exact same method!
We can now select more than one type for our enum using this flag system!
If you want to assign its value via code rather than in the inspector we use the bitwise or operator “|”.
This will set the type to both FIRE and GRASS.
Now if we want to check if our multi-type monster has FIRE as one of its types we can use the following:
Has flag will compare with each of its flags to see if it is included.
Why Does This Work?
In a simplified explanation, each number in a bitmask represents a bool. 0 being off, and 1 being on.
In this case, the furthest 1 represents FIRE. The second to last 1 represents WATER. So in our FIRE/WATER combo, notice that both the FIRE flag, and the WATER flag are both turned on, while all other flags are turned off. If we add their values before converting FIRE being 1 in this case and WATER being 2, we get 3. If we convert 3 into binary we get 00000011, which is our value for FIRE/WATER.
Hopefully this helps you create a cleaner dual typing system, I tried to keep it as much on the simple side as possible. Feel free to reach out if you have any comments or Questions!