Enums
Summary
An enum (enumeration) is a type whose values are restricted to a small,
named, fixed set of options — GameState.Menu, GameState.Playing,
GameState.Paused instead of the raw integers 0, 1, 2. It exists to
replace "magic numbers" and ad-hoc string constants with something the
compiler can check, so an invalid state is caught at compile time instead
of discovered at runtime.
Mental Model
Think of an enum as a multiple-choice question with a fixed answer sheet.
A variable of type int can hold any whole number, which is like asking
an open-ended question — nothing stops someone from writing "42" as the
game's state even though only three states make sense. An enum variable can
only hold one of the options printed on the sheet: the compiler physically
will not let GameState state = 42; compile, because 42 isn't one of the
named choices.
Under the hood, each option is still just an integer (Menu = 0,
Playing = 1, Paused = 2 by default) — the enum is a thin, named wrapper
around that integer that exists purely to make invalid values
unrepresentable and switch statements exhaustive-checkable.
Core Explanation
- A closed set of named values.
enum GameState { Menu, Playing, Paused }declares exactly three valid values; there is no way to construct a fourth one without editing the enum's own definition. - Backed by an integer by default. Each member gets an implicit integer
value starting at
0in declaration order, unless explicit values are assigned (enum Rarity { Common = 0, Rare = 10, Legendary = 100 }) — useful when values are serialized to save files or network messages and must stay stable even if new members are inserted later. - Pairs naturally with
switch. Because the set of values is closed and known, aswitchover an enum lets tools warn when a case is missing — a real safety net that a rawintorstringcannot offer. - Flags enums for combinable options. Marking an enum
[Flags](C#) and giving members power-of-two values (1, 2, 4, 8, ...) allows bitwise combination —DamageType.Fire | DamageType.Poison— for cases where more than one option can be true simultaneously. This is a distinct use case from a plain enum's "exactly one of these" semantics.
Syntax / Examples
C# (Unity)
public enum GameState
{
Menu,
Playing,
Paused,
GameOver,
}
public class GameManager : MonoBehaviour
{
public GameState CurrentState = GameState.Menu;
public void SetState(GameState next)
{
switch (next)
{
case GameState.Menu:
Time.timeScale = 0f;
break;
case GameState.Playing:
Time.timeScale = 1f;
break;
case GameState.Paused:
Time.timeScale = 0f;
break;
case GameState.GameOver:
Time.timeScale = 0f;
break;
}
CurrentState = next;
}
}
C++ (Unreal)
UENUM(BlueprintType)
enum class EGameState : uint8
{
Menu,
Playing,
Paused,
GameOver
};
void AGameManager::SetState(EGameState Next)
{
switch (Next)
{
case EGameState::Menu:
case EGameState::Paused:
case EGameState::GameOver:
UGameplayStatics::SetGlobalTimeDilation(this, 0.0f);
break;
case EGameState::Playing:
UGameplayStatics::SetGlobalTimeDilation(this, 1.0f);
break;
}
CurrentState = Next;
}
Unreal Blueprint
A UENUM(BlueprintType) C++ enum (as above) becomes selectable as a
variable type in Blueprint, complete with a Switch on EGameState node
that auto-generates one execution pin per enum value — the visual
equivalent of a switch statement's cases.
Practical Example
An item's rarity should control both its display color and its drop weight, without magic numbers scattered through the codebase.
public enum Rarity
{
Common,
Uncommon,
Rare,
Legendary,
}
public static class RarityInfo
{
public static Color GetColor(Rarity rarity) => rarity switch
{
Rarity.Common => Color.white,
Rarity.Uncommon => Color.green,
Rarity.Rare => Color.blue,
Rarity.Legendary => new Color(1f, 0.5f, 0f),
_ => Color.magenta, // Visibly wrong — flags an unhandled case.
};
}
RarityInfo.GetColor is exhaustive over every current Rarity value; if a
fifth rarity is added later without updating this method, the fallback
magenta makes the gap obvious in-game instead of silently defaulting to
white.
Common Mistakes
- Relying on an enum's underlying integer value directly (
if (rarity == 2)instead ofif (rarity == Rarity.Rare)) — it compiles, but reads like a magic number again and breaks silently if the enum's member order ever changes. - Reordering or inserting members in an enum whose values are already
serialized (save files, network messages,
PlayerPrefs). InsertingUncommonbeforeRareshifts every value after it, silently corrupting any previously saved data that stored the old integer. Append new values at the end, or assign explicit stable values, instead. - Using an enum where a
[Flags]combination was actually needed (or vice versa) — a plain enum represents exactly one choice; reaching for bitwise|on a non-flags enum produces a nonsensical value that isn't any of the declared members. - Forgetting a
default/fallback case in aswitchover an enum that might grow — without one, adding a new member later means every existing switch silently does nothing for it until each call site is manually revisited.
Best Practices
- Give the first/default member a sensible default meaning (
Menurather thanPlayingas the C# default of0) since an uninitialized enum field defaults to its zero-value member. - Assign explicit integer values to any enum whose values will be serialized, so future insertions can't silently shift and corrupt saved data.
- Prefer a
switchover anif/else ifchain when branching on an enum — it documents that every case is being considered and lets tooling flag missing ones. - Reserve
[Flags]enums for genuinely combinable options; if only one value is ever valid at a time, a plain enum is the correct — and more restrictive, therefore safer — choice.
Exercises
- Write a
[Flags]enum forDamageType(Fire, Poison, Physical, Ice) and a method that returnstrueif a given combined value includesFire. Explain why a plain (non-flags) enum could not express "this attack deals Fire and Poison damage." - Take the
GameStateenum above and add aLoadingstate. Update theSetStateswitch and explain, from the compiler's behavior, how you'd notice if you forgot to handle it somewhere else in the codebase. - Explain, in writing, why inserting a new member in the middle of an already-shipped enum is dangerous for a game that has existing player save files, and propose two different fixes.
- Convert a boolean pair (
isPaused,isGameOver, both mutually exclusive withisPlaying) into a singleGameStateenum, and explain what invalid combination of booleans the enum makes impossible to represent.
Related Concepts
- Structs — enums are often stored as a field inside a struct or class to describe one aspect of that value's state.
- Access Modifiers — enum members are implicitly public; understanding access modifiers clarifies why an enum's type can still be restricted even though its values cannot.
- Static Members — a lookup table keyed by an enum (as in the
RarityInfoexample) is commonly implemented as a static dictionary/method.
References
KNOWLEDGE_SCHEMA.md— GDKB's concept maturity ladder and cross-linking rules, which this document follows.- Unity Scripting Reference — Enumerations: https://docs.unity3d.com/Manual/TypesFolderLocation.html
- Unreal Engine C++ documentation — Enums (
UENUM): https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-uproperties