Do you enjoy coding, or at least admiring pretty Unity inspectors? How about exploring game design, or the secrets of life itself? My name is Ryan, and my work here at Chronosapien falls on the programming side of things. I’ll be jumping between devlogs covering coding tutorials, design processes, working with Unity, and Shapesong—our current passion project.
Now if you’re already turned off, don’t worry—the programming side of this is optional, and at the end of this devlog series comes a take-home package of code you can use directly in your own Unity projects. So, feel free to skip the coding explanations if you desire—the important thing is understanding when and how to use these scripts. If you do jump on board for the programming side, we will be walking through more than just the solution—these posts will be covering the workflow from start to end.
I’ll start these devlogs with a topic that comes up quite often for us—enums. Enums, if you’re unfamiliar, are basically a convenient way to group labeled values (or options) together for assigning to a variable. Specifically, we’ll focus on flagged enums: enums that can store multiple values. These are also called combined enums, masked enums, bitwise enums… there’s not much of a standard. We’ll follow through the life of a flagged enum and make sure it’s supported every step of the way. You want to be a good code-daddy now, don’t you?
- How do we create flagged enums in C# and Unity, and what are they good for?
- This is a basic meat and potatoes post on enums.
- Now that we have a flagged enum, how can we assign values to it outside of code?
- This is the core of the enum topic, and comes with some take-home goodies at the end. A must see!
- How can we make working with enums in code a breeze?
- This post will cover a whole slew of methods that make dealing with enums trivial, as well as what to consider when making your own methods. Expect even more take-home goodies.
- Now that we’ve mastered the use of enums, where do we draw the line for what they’re capable of? And, more importantly, what exists on the other side of that divide?
Pt. 1: Enum Basics
What are enums?
If you’re unfamiliar with enums, I’ll include a brief overview before pointing towards additional materials. (If you already know all there is to know about enums, feel free to skip on ahead.) There are two keywords that you’ll become familiar with: enum and Enum. While I’ll use them interchangeably in this devlog, these are two separate things in code. The enum keyword is used to declare your own “enumerator list” (or the set of entry values). The Enum keyword is the System.Enum class, and is the base class for all enums. Enum will almost never be used directly, instead being used almost entirely for its static methods, such as Enum.GetNames. As for IEnumerable, IEnumerator, or Enumerable (System.Linq namespace): don’t confuse these with enums; these are for dealing with collections and “enumerating” (walking) over their values.
Enums have an “underlying type”, int/Int32 by default. In most cases (especially in Unity), it is advisable to keep int as the underlying type—nearly all methods you encounter will be expecting your enum to be built on int. The underlying type can be declared like this:
While this looks like Vehicle is inheriting from int, that is not the case—it’s actually syntactic-sugar for assigning the underlying type. In fact, enum is just syntactic-sugar for a whole mess of functionality. But we don’t need to go down that road—just stick to int. And before you try it, no; you can’t make enums inherit from other enums (or anything, by that matter).
It is worth mentioning that you are limited to how many entries you can have for a flagged enum: 32, not counting 0. This is due to int being 32 bits, and each entry in a flagged enum being assigned to one bit. This is one of the few reasons you would ever need to use a different underlying type for an enum.
What is important is that your enum can be cast to and from its underlying type explicitly using a cast operation. This looks like:
Flagged enums have two other convenient values to cast from. These are 0 and ~0, and correspond to “None” and “All”, respectively (assuming your flagged enum was set up properly).
Bitwise math for enums—including contains, add/union, remove, difference, etc.—will be covered in further detail in “Pt. 3: Ease of Use in Code.” For now, that’s enough information to move on to the meat of this devlog—declaring flagged enums in Unity.
So, how are they declared?
So now that we understand the basics, let’s cover making our own enums in Unity. For this example, we’ll be using Vehicle. The standard enum for Vehicle looks like this:
We have a few vehicles, with a few overlaps to handle some other commonly used terms for the same value (BigAssTruck == Pickup, Minivan == Van). But before we move on, let’s clean it up a little. By explicitly defining the values, the new enum looks like this:
So now that we have a standard enum, how do we get a flagged enum set up in code? There’s only two things that need to be done differently from regular enums: values must be declared explicitly as powers of two, and the [Flags] Attribute (System.FlagsAttribute) should be used. It’s also a good convention to make the Type name of non-flagged enums singular, while flagged enums are plural (for intent and readability). The flagged enum version of Vehicle looks like this:
As you can see, not much has changed. In order to declare values as powers of two, we handle bit shifting with the << operator, the “bitwise left shift operator.” I know, right: fucking mouthful. Basically, it means we’re taking the value of 1 (left side of <<) and shifting it by a number of bits (right side of <<) to make a power of two (e.g. 1 << 4 == 24 == 16). You’ll see a few other standards, seen here below:
Of Extra Fabulous Comics, Zach. 200. Extra Fabulous Comics. N.p., n.d. Web. 7 July 2017. http://www.extrafabulouscomics.com/comic/200/ (lol mla)
If you noticed that our first entry is now equal to a value of 1 instead of 0, kudos to you. This is because a flagged enum should always use 0 to mean a value of “None”. Microsoft will tell you that you should have a “None” value specifically declared, but I’d advise against it. A flagged enum should never have a value assigned to 0 unless it is “None” or “Nothing,” and neither of those are relevant to the actual value entries of the enum. Unity’s popup will handle automatic options for “Nothing” and “Everything” by default, so we’d just have to do additional work to remove the “None” entry in the popup. You can always directly check vehicles == 0 anyways, and it’s never good to rely on all enums having a value declared for 0. Besides, it’s fun to play the rebel.
What are they good for?
We know what enums and flagged enums are now. So first, what do flagged enums replace, and second, why bother to use Vehicles over List of Vehicle?
Flagged enums are commonly used to replace laundry lists of Boolean flags (such as combining isTedious, isCodeSmell, and hasCopyPasteProblems all into one variable). They’re also used when you need to indicate which values of an enum are valid (such as only allowing Vehicles.Convertible, Vehicles.Hatchback, and Vehicles.Compact as possible user selections).
I’ll just keep this one brief: there’s about a dozen reasons to favor Vehicles over List of Vehicle
Seriously? Well if you are interested, here's a collection of some of the better links I found:
Did I hear a Pt. 2 mentioned?
So now that we know how to make a flagged enum, how do we get it to work with the Inspector? As we’ll see next week, that’s actually a loaded question—and one few people get correct. This post was limited to the basics, but the next post is going to go deep down the rabbit hole—we’ll be covering how to write your own PropertyDrawer for flagged enums, problems to look out for when working with PropertyDrawers, and how to extend functionality through Attributes. On top of all that, every participant will walk away with their very own AssetPackage containing a multitude of fully documented Unity scripts dealing with Enums, including many easy-to-follow examples. So don’t miss out on next week’s post! If that's not enough, here's a taste of what you'll be walking away with: