We briefly mentioned the enum in our last lesson, so I felt like now would be a great time to cover it in greater depth. At the same time, we can expand on the topic and introduce Flags (bit masks). Some of this gets a bit deeper into the nerdy side of programming, but I will try to keep everything easy to understand.
The “enum” type
I first introduced the enum in the previous lesson with the “Difficulties” settings:
public enum Difficulties { Easy, Medium, Hard, Count }
This bit of code can be placed outside of a class and it becomes a “global” type which can be used by any of your scripts. You could also place it inside of a class, but then to access it, you need to reference the enum through dot notation.
// This form can be used when Difficulties is not contained in a class // or when it is defined in the current class Difficulties d1 = Difficulties.Easy; // This form is used when Difficulties is defined in a class // other than the current class Demo.Difficulties d2 = Demo.Difficulties.Easy;
The type of the enum is unique to itself, although it has an underlying type (by default an ‘int’). It is possible to change the underlying type in the declaration such as specifying “byte” which you could do if you know you won’t have a large range of values which need to be represented. The form looks pretty similar to class inheritance, where the underlying type is referenced after a colon such as in the following example:
public enum Difficulties : byte { Easy, Medium, Hard, Count }
With an instance of a class, you can assign it to a reference of a “base” type without casting. This is not true with enums – instead you must cast it or else you will see an error: “error CS0266: Cannot implicitly convert type `Demo.Difficulties’ to `byte’. An explicit conversion exists (are you missing a cast?)”
void Start () { // Implicit conversion of an instance to base type is ok MonoBehaviour script = this; // Implicit conversion of an enum to an underlying type is not ok... // byte value = Difficulties.Easy; // ... Use this instead byte value = (byte)Difficulties.Easy; }
The values in our enum begin with the default value of the underlying type (‘0’) and count up, although you can specify any value which the underlying type can contain. Left alone, our Difficulties values would range from 0 to 3. To specify values, use the following form:
public enum Multipliers { Negative = -1, Nullify = 0, Positive = 1 }
Note that because the values auto-increment, I could also have only specified the “Negative” value, and the other entries would have been correctly assigned.
Bits and Bit Shifting
One of the neat uses of enums comes by way of using them as a bit mask. However, before we begin covering that, it may be useful to have a general understanding of what is going on under the hood.
Bits refer to the binary sequence of 0’s and 1’s that together are used to define more complex things. Data-types require a certain number of bits to be fully represented. A ‘byte’ for example, requires 8 bits like the following: 00000000
The numerical value of the previous sample was zero, and as you turn on (set bits to 1 at various locations) you get combinations making the other numbers. Here are a few numbers in order with their associated bit pattern:
00000000 = 0 00000001 = 1 00000010 = 2 00000011 = 3 00000100 = 4
Hopefully you can see the pattern here, where you increment to a one, then move a place over and reset the other bit to 0 to continue incrementing the numerical value.
Some programmers look at this and think, “I could use this sequence of bits like an array of bool” – and without needing the equivalent amount of storage (note that a ‘bool’ requires 8-bits). So basically they are looking at each bit position and saying, is this bit a 0 (false) or 1 (true).
Some simple math can reveal the position of each bit: 2 to the power of the index of the bit you want to set, remember to start counting from zero:
00000001 = 2^0 = 1 00000010 = 2^1 = 2 00000100 = 2^2 = 4 00001000 = 2^3 = 8
But if you don’t want to remember the math, you can use something called bit-shifting. You will use “<<” to shift a bit to the left. We will use this method for declaring the values of enum flags in a moment:
00000001 = 1 << 0 00000010 = 1 << 1 00000100 = 1 << 2 00001000 = 1 << 3
Flags (Bit Mask)
Sometimes you will see an enumeration marked with “Flags” as in the following:
[System.Flags] public enum Colors { None = 0, Red = 1 << 0, Green = 1 << 1, Blue = 1 << 2 }
The ‘Flags’ marker has no effect on the enum itself, or the default values that are assigned to its elements – Note that I still had to manually assign the power of 2 values. Under the hood, this ‘Colors’ enum still has an underlying type of ‘int’ and can hold any value an ‘int’ can hold and can have its elements assigned to any value you like within that range, just as you could have done without using the ‘Flags’ marker. The reason you use the marker is to enable other functionality such as modifying the output of ToString()
which can output the names of the combined flags rather than the numerical equivalent of the underlying type.
Because I defined each element as a power of two, this enum can be treated as a ‘Bit Mask’, which means that you can specify any combination of the elements (and easily turn them on or off at any time), rather than pointing only to a single element at a time.
Several features you will frequently want are shown in code below:
void Start () { // Create a variable to hold our Bit Mask Colors c = Colors.None; // Turn a bit on c |= Colors.Red; // Or directly assign a combination of bits c = Colors.Blue | Colors.Green; // This can also be used in the definition of new elements within the enum type itself // Turn a bit off c &= ~Colors.Blue; // Check whether or not a bit is 'on' if ((c & Colors.Green) == Colors.Green) { // The flag is 'on' } }
Some of this looks a bit hard to read, so I will explain it now. The vertical line ‘|’ is read ‘OR’ which means that you are modifying the sequence of bits to be ‘on’ in any location that either of the other numbers bits had been ‘on’.
So for example, this line c = Colors.Blue | Colors.Green;
looks at the two bit sequences to make a third bit sequence where any bit that was a ‘1’ from either value will be a ‘1’ in the final output:
00000010 - Green 00000100 - Blue -------- // Perform an OR so that a one in any column is passed along 00000110 - Green, Blue
The ‘&’ is read ‘AND’ but is very different from ‘OR’ because it requires that the same bit (index-wise) be enabled on both numbers in order for the final output to also be enabled. Since Green and Blue have different indexes, using ‘AND’ on them would result in zero, or ’Colors.None’.
The ‘~’ is read ‘NOT’ and refers to a bit sequence which is opposite of the current sequence (every 0 becomes a 1 and vice-versa). We used a combination of ‘AND’ and ‘NOT’ to remove a flag, which works like this:
00000110 - Green, Blue // Our starting value has two flags enabled 11111011 - This is ‘NOT’ Blue -------- // Perform an AND on these two sequences leaves only the bits which are both enabled 00000010 - Back to only Green
BitMasks and Unity
Unity can serialize your enum’s just fine, unfortunately, it doesn’t auto provide a mask-selection type inspector by default. You will get a nice named drop down list to select a single entry at a time, but not the ability to select multiple entries as you would with a Camera culling mask as one example. You can however write a custom inspector for your script and expose a property using EnumMaskField
to get the functionality you want.
For example, your script “Demo.cs”:
using UnityEngine; using System.Collections; [System.Flags] public enum Colors { None = 0, Red = 1 << 0, Green = 1 << 1, Blue = 1 << 2, } public class Demo : MonoBehaviour { public Colors color; }
Can be coupled with a special inspector script “DemoInspector.cs” (Note that this script must be located inside a folder called “Editor” to work:
using UnityEngine; using UnityEditor; using System.Collections; [CustomEditor (typeof(Demo))] public class DemoInspector : Editor { public override void OnInspectorGUI () { Demo demo = (Demo)target; demo.color = (Colors)EditorGUILayout.EnumMaskField ( "Colors", demo.color ); } }
Summary
In this lesson I covered enums and their uses, including use as bit-masks. We took an in-depth view of how the bits are represented and covered common use scenarios like turning bits on and off or checking the status of a bit. Some of these features are not as “readable” as other coding options, but they are usually more efficient, so they make sense to use in certain scenarios. Finally, we showed how Unity can use a bit mask in editor by writing a custom editor script.