Summary
An enum (enumerated type) is a named set of fixed, related values. Rather than representing a game state as the integer 2 and hoping you remember what that means, an enum lets you write GameState.GameOver — self-documenting, type-safe, and impossible to set to an invalid value. Enums are the go-to pattern for anything that can be in one of a limited set of named states: game phase, enemy behaviour, weapon type, dialogue mood.
Key ideas
Declaration:
enum GameState
{
MainMenu,
Playing,
Paused,
GameOver
}By convention, enum names are PascalCase and live outside any class (or at the top of the file, before the class). Members are also PascalCase. C# internally stores each member as an integer starting at 0, but you never need to work with those numbers directly.
Usage:
GameState currentState = GameState.Playing;
if (currentState == GameState.Paused)
{
Time.timeScale = 0;
}Enums in switch statements:
switch is the natural companion to enum — it forces you to handle every case explicitly.
switch (currentState)
{
case GameState.Playing:
// run normal game update
break;
case GameState.Paused:
// show pause menu
break;
case GameState.GameOver:
// show game over screen
break;
case GameState.MainMenu:
// show main menu
break;
}Enums in Unity Inspector:
If a field of enum type is public or marked [SerializeField], Unity displays it as a dropdown in the Inspector — no extra work required.
public GameState startingState = GameState.MainMenu;This makes enums excellent for configuring initial state in the editor without magic numbers.
In practice
Full GameManager example:
public enum GameState
{
MainMenu,
Playing,
Paused,
GameOver
}
public class GameManager : MonoBehaviour
{
public static GameManager Instance;
private GameState currentState = GameState.MainMenu;
private void Awake()
{
Instance = this;
}
public GameState GetState() => currentState;
public void SetState(GameState newState)
{
currentState = newState;
switch (currentState)
{
case GameState.Playing:
Time.timeScale = 1f;
break;
case GameState.Paused:
Time.timeScale = 0f;
break;
case GameState.GameOver:
Time.timeScale = 0f;
break;
}
}
}Other scripts call GameManager.Instance.SetState(GameState.Paused) to trigger a state change — the GameManager handles the consequences centrally.
Enemy behaviour state:
public enum EnemyState { Idle, Patrolling, Chasing, Attacking, Dead }
public class EnemyAI : MonoBehaviour
{
private EnemyState state = EnemyState.Patrolling;
private void Update()
{
switch (state)
{
case EnemyState.Patrolling: Patrol(); break;
case EnemyState.Chasing: Chase(); break;
case EnemyState.Attacking: Attack(); break;
}
}
}Evidence
From Miles (2019, §4.2.1): “C# has a way in which we can create a type which has just a particular set of possible values. These types are called ‘enumerated types’.”
From Miles (2019, §4.2.2, Programmer’s Point): “Every time that you have to hold something which can take a limited number of possible values, or states… you should think in terms of using enumerated types to hold the values. The program becomes simpler to write, easier to understand and safer.”
Gotchas
- An enum variable can be cast to/from
int, which bypasses type safety:GameState s = (GameState)99;compiles even though 99 is not a valid state. If you need to validate incoming values (e.g., from save files), useEnum.IsDefined. - Enum values default to starting at 0. If you serialize enum values to a save file or database, adding a new member in the middle will shift all subsequent values and corrupt old data. Always add new members at the end, or assign explicit integer values:
GameOver = 3. - Enums cannot have methods attached to them in standard C# (unlike Kotlin or Swift). For complex state behaviour, prefer a class-based state machine with full classes.
Related
- csharp-oop-fundamentals — classes and access modifiers
- csharp-control-flow — switch and if/else that drive enum-based logic
- unity-gamemanager-pattern — central game state management using enums
- unity-animator-scripting — using enums to drive Animator parameter values
- csharp-inheritance — advanced state handling with polymorphism instead of switch