What are Enum Flags
When we define an enumeration we create a set that represents mutually exclusive values. For example we may need to create different behaviors for an enemy. To do that we define an enum like this:
public enum Behavior
{
Idle,
LookingForPlayer,
Moving,
Attacking
}
and then we define a Behavior type variable:
public Behavior EnemyBehavior;
This is serialized by Unity, so that we can use it and change its value in the Editor:
The EnemyBehavior can have any of those values, but cannot contain two or more at the same time. In this example that is ok, because an enemy cannot have two different behaviors at the same time. But what if we need to represent values that are not mutually exclusive.
Let us suppose that we have a tile based game and the player can choose to go towards any of the four cardinal directions, but only if the tile allows it. For example a tile that is at the eastern edge of our map would not allow the player to move east, or a tile that North and South of it has tiles that represent impassable terrain, for example mountains, would only allow the player to move East and West. Representing the directions for each tile that a player is allowed to move with an enum, would be a problem, because a variable of our enum type can only hold one of those directions.
Representing those directions in our tiles with booleans is messy, there is nothing that ties those variables together and even if we create our own type that holds those booleans we increase the chance of bugs by forgetting to set each of those every time we need to describe new allowed directions. This solution also doesn’t scale very well, what if we want to have eight different directions in each of our tile, or we have a use case that a variable can have even more than eight different values and be allowed to have a combination of those values as well. Those are cases where Enum flags can be really useful.
Enum flags usage
By using the FlagsAttribute
we indicate that our enum consists of bit fields. By doing that, we can use bitwise operations that can combine values or check for the existence of one of those values in a variable that holds a combination. To use the Flags
attribute, we have to declare our enum, in a way that each of its values is represented by a bit set to 1 and all other bits set to 0. This can be done easily with two different ways:
[Flags] public enum Directions
{
None = 0,
North = 1,
West = 2,
South = 4,
East = 8
}
or
[Flags] public enum Directions
{
None = 0,
North = 1 << 0,
West = 1 << 1,
South = 1 << 2,
East = 1 << 3
}
Those two ways are equivalent and it is a matter of preference. The above code means that the North value will be a number that has all its bits set to zero, except the first, that will be one. West will be a number that has all its bits set to zero, except the second that will be one and so on. By using the Flags
attribute we tell the compiler that he should treat the enumeration in a way that its values are not exclusive, so that the combination of those values gets displayed properly. For example the following code
AllowedDirections = Directions.East | Directions.West;
Debug.Log(AllowedDirections);
in Unity, will log West,East
, but if we hadn’t used the flags attribute then it would log 10
, which is the bitwise OR operation between 2 and 8.
A thing to notice here is the inclusion of the None value. Because C# always initializes our variables by setting all their bits to zero, the inclusion of the None value is a good idea, even if it doesn’t make sense in our code. Instead of None, it could indicate an invalid value, when we have forgotten to initialize one of our variables.
Operations between values in an Enum Flag
By doing bitwise operations in our enum flag values, we can add/remove a value in a variable, or check if a value is set.
We can add a value in an existing variable by using the bitwise OR operator (|
) like this
AllowedDirections = Directions.East;
AllowedDirections = AllowedDirections | Directions.West;
or by using the compound assignment:
AllowedDirections = Directions.East;
AllowedDirections |= Directions.West;
with the above code we had created an AllowedDirections variable that contained the East direction and by using the bitwise OR operator our variable now holds both the the East and the West values.
To remove a value the code is equally simple
AllowedDirections = Directions.West | Directions.East;
AllowedDirections = AllowedDirections & ~Directions.West;
or in a shorter form
AllowedDirections = Directions.West | Directions.East;
AllowedDirections &= ~Directions.West;
the above code will remove the West value from our AllowedDirections
variable that contained the West, East
values, so that now will equal to East
.
If this code seems confusing here is what happens:
At first our AllowedDirections
variable has this representation (omitting the zeros at the beginning)
1010
and the Directions.West
value has this representation:
0010
by using the bitwise NOT operator (~
) we reverse the bits in the Directions.West
value, the zeros become ones, and the ones become zeros. So the ~Directions.West
is represented like this:
1101
Then by using the bitwise AND operator (&
) we get :
1010 AND
1101 =
---------
1000
that is, we set to 1 only the bits that are 1 in both values.
To check if a value exists in a variable
The bitwise operation is coded like this:
(AllowedDirections & Directions.East) == Directions.East
if the AllowedDirections
variable contains the Directions.East
value the above statement returns true.
C# also has the HasFlag
method that executes the above logic, so the following code is equivalent:
AllowedDirections.HasFlag(Directions.East)
Something to have in mind here: If you want to check for the None
Value, both of the above will return true, no matter the values that exist in the AllowedDirections
variable. That happens because the bitwise AND operation with something that has all its bits set to zero (the None
value), will always be equal to zero, no matter the value that we are checking. If we want to check if our variable has the None
value, then we can use the Equals method to get the correct result:
AllowedDirections.Equals(Directions.None)
Checking If A Value Exists In An Enumeration
The static Enum.IsDefined method allows us to check if a value exists in an enumeration. The problem is, that if we are using the flags attribute, then this method returns false if we check for a value with multiple bit fields set. For example:
Console.WriteLine($"{Directions.North} Exists: {Enum.IsDefined(typeof(Directions), Directions.North)}");
Console.WriteLine($"{(Directions)15} Exists: {Enum.IsDefined(typeof(Directions), (Directions)15)}");
Console.WriteLine($"{(Directions)16} Exists: {Enum.IsDefined(typeof(Directions), (Directions)16)}");
Will output:
North Exists: True
North, West, South, East Exists: False
16 Exists: False
If we want to be able to check for composite values, we can use the following static method:
bool IsFlagDefined (Enum value) => !int.TryParse(value.ToString(), out _);
This works because when an enum value does not exist, then the value parameter will stay as an integer that has the ToString
method called, and it will be parsed by the TryParse
method. Here’s an example with the previous values:
Console.WriteLine($"{Directions.North} Exists: {IsFlagDefined(Directions.North)}");
Console.WriteLine($"{(Directions)15} Exists: {IsFlagDefined((Directions)15)}");
Console.WriteLine($"{(Directions)16} Exists: {IsFlagDefined((Directions)16)}");
and the result:
North Exists: True
North, West, South, East Exists: True
16 Exists: False
Bit representation of an Enum Flag variable
If you want to see the binary representation of an enum flag variable, C# has the Convert.ToString
method that we can use to check our variables’ representation in binary. Its usage in our case would be like this:
var stringRepresentation = Convert.ToString((int)AllowedDirections, 2);
How to use Enum flags in Unity
In Unity the Flags
attribute is also being used by the editor, so we can easily change values:
this allows us to select more than one value in the editor, but there is a potential problem here. Unity by default adds the Everything
value as you can see in the image. If someone selects it in the Editor, Unity will automatically select the Everything
value and all the values that we have defined, except the None
value.
Someone would assume that this value is equal to a variable that has all our values, but this isn’t true. The Everything
value doesn’t equal to AllowedDirections = Directions.North | Directions.West | Directions.South | Directions.East
The reason is this:
Our AllowedDirections
variable has the following representation in binary:
0000 0000 0000 0000 0000 0000 0000 1111
but the Everything
value is represented like this:
1111 1111 1111 1111 1111 1111 1111 1111
The Everything
value sets all the bits to 1, so that it can contain future additions to our enum. That is an implementation decision by Unity that we should be aware of. If we want to check if a variable has all the values that we have defined in our enum, the right way to do it, would be:
- To either create in our enum a value that is the bitwise OR of all our values and take care so that no one selects the
Everything
value in the editor, but uses our own. Or - sanitize the value every time that is being selected in the editor, like this:
private const Directions ALL_DIRECTIONS = Directions.North | Directions.West | Directions.South | Directions.East;
AllowedDirections &= ALL_DIRECTIONS;
The first way has the risk of someone selecting the Everything
value by mistake, the second way is safe from this mistake, but hurts our code’s extensibility, because we have to remember that every time we change the values our enum holds, to also change our const variable to represent the current all values.
This is it about enum flags and their usage in Unity. Thank you for reading and as always for questions and comments, use the comments section, or contact me directly via the contact form or email, also if you don’t want to miss any of the new articles, you can always subscribe to my newsletter or the RSS feed.