Enums & Race Phases: State Machine Intro

In this lesson you'll learn to

  • ✓Model Lobby→Countdown→Racing→Finished as an enum and switch behavior per phase.

Model Lobby→Countdown→Racing→Finished as an enum and switch behavior per phase.

📖 Reference & full walkthrough: Enums & Race Phases: State Machine Intro

🔁 Builds on: south-shores enum-with-data-pattern reference (anchor-beach)

🧩 Your capstone piece: race_phase enum driving the whole game loop


What you'll learn

You'll learn how to define an enum in Verse to represent a fixed set of game states, and how to drive real on-screen UI from it. Each trigger press advances the state, and we'll display the new state name to every player by building a text_block widget, wrapping it in a canvas, and adding it through GetPlayerUI[].AddWidget.

How it works

An enum (enumeration) is a custom type made of a fixed set of named values. Instead of remembering that 1 means "Playing", you write game_state.Playing. The compiler guarantees a variable of that type can only ever hold one of the listed values — that's the type safety.

Key pieces in the code below:

  • Defining an enum: game_state := enum: MainMenu Playing Paused GameOver declares the type. Access a value with game_state.MainMenu.
  • Mutable state: var CurrentState : game_state holds the active state. We reassign it with set CurrentState = ... (a bare = on a var is an error).
  • Comparison: enum values compare with = (single equals), never ==.
  • Showing UI: GetPlayerUI[Player] is a <decides> call, so it lives in an if condition. It returns a player_ui whose AddWidget accepts a widget — we hand it a canvas that contains our text_block.

Let's build it

This device subscribes to a trigger. Each press advances the enum and refreshes a centered text widget on every player's HUD showing the current state name.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }

# A fixed set of named game states — no magic numbers.
game_state := enum:
    MainMenu
    Playing
    Paused
    GameOver

enum_ui_device := class<concrete>(creative_device):

    # Link a trigger in the editor; stepping on it advances the state.
    @editable Trigger : trigger_device = trigger_device{}

    # The current state, starting at MainMenu. `var` so we can reassign it.
    var CurrentState : game_state = game_state.MainMenu

    OnBegin<override>()<suspends>: void =
        # TriggeredEvent passes an optional agent; we react on every press.
        Trigger.TriggeredEvent.Subscribe(OnTriggered)

    # Advance the enum, then refresh the UI for everyone.
    OnTriggered(MaybeAgent : ?agent): void =
        # Cycle the state using `=` comparisons and `set` to reassign.
        if (CurrentState = game_state.MainMenu):
            set CurrentState = game_state.Playing
        else if (CurrentState = game_state.Playing):
            set CurrentState = game_state.Paused
        else if (CurrentState = game_state.Paused):
            set CurrentState = game_state.GameOver
        else:
            set CurrentState = game_state.MainMenu

        # Pick a readable label for the active enum value.
        StateLabel := GetStateName(CurrentState)
        Print("State changed to: {StateLabel}")

        # Push the label onto every player's screen.
        for (Player : GetPlayspace().GetPlayers()):
            if (UI := GetPlayerUI[Player]):
                # Build the text widget showing the current state.
                StateText := text_block{DefaultText := StringToMessage(StateLabel)}
                # A canvas lets us position the widget on screen.
                StateCanvas := canvas:
                    Slots := array:
                        canvas_slot:
                            Widget := StateText
                            Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.1}, Maximum := vector2{X := 0.5, Y := 0.1}}
                            Alignment := vector2{X := 0.5, Y := 0.5}
                            SizeToContent := true
                # AddWidget renders the canvas on this player's HUD.
                UI.AddWidget(StateCanvas)

    # Wrap a string in a message so text widgets accept it.
    StringToMessage<localizes>(Value : string): message = "{Value}"

    # Map each enum value to a display string (enums don't auto-stringify).
    GetStateName(State : game_state): string =
        if (State = game_state.MainMenu) then "Main Menu"
        else if (State = game_state.Playing) then "Playing"
        else if (State = game_state.Paused) then "Paused"
        else "Game Over"```

## Try it yourself

1. Place this device in your island.
2. Drop a `trigger_device` and link it to the `Trigger` field in the device's Details panel.
3. Launch a session and step on the trigger.
4. Each press updates the centered HUD text — `Playing`, then `Paused`, then `Game Over`, then back to `Main Menu` — and logs the same value to the output log.

## Recap

- Define a fixed value set with `name := enum: ValueA ValueB`.
- Access a value with `EnumName.ValueA`; compare with `=` (not `==`).
- Store an enum in a `var` and reassign it with `set State = ...`.
- Enums don't auto-convert to strings — write a helper that maps each value to text.
- Drive real UI by binding `GetPlayerUI[Player]` in an `if`, then calling `AddWidget` with a `canvas` containing a `text_block`.
🔒

Keep going — free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/sug-defining-and-using-enums-in-verse
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🐻 North Jungle expedition.

⛵ Take me there