Verse Wiki — the Verse handbook for Blueprint authors
Chapter 3 · Lesson 11

logic & enum: The Two-Sided Coin and the State Nameplate

Blueprint's Branch node consumes a True/False boolean; Verse's if pointedly doesn't — it consumes «can this wire fire». That logic slot in your backpack gets pulled out for a proper look this lesson: whether the box is checked (true/false) is a value, while whether this wire can fire is something else entirely — one of the most fundamental ideas in Verse. We'll also unlock enum and hang proper nameplates on your game states, waving goodbye to magic numbers for good.

1. logic: A Coin with Only Two Faces

Back in the variables lesson, logic made a cameo as one of the four slot types: it's the Boolean you know from Blueprint — the checkbox-style switch whose only possible values are true and false, built for recording black-and-white facts — is the door open, do we have the key, is the player ready. Opening slots, making constants, comparing values: it plays by the same rules as int and string:

switch_room_device.verse
using { /Fortnite.com/Devices }

switch_room_device := class(creative_device):

    # logic slots: hold only true or false
    var IsDoorOpen:logic = false
    var HasKey:logic = true

    # No var means a constant: this light stays on forever
    LightAlwaysOn:logic = true

▸ Blueprint translation: this is like adding two Boolean variables in the variables panel — IsDoorOpen (unchecked by default) and HasKey (checked by default); that last LightAlwaysOn has no var, making it a constant with no Set node, locked to true.

To compare two logics, use = and <>, as in if (A = B):. Careful: these comparisons aren't computing a true/false to hand you — they're asking «can this wire fire?». If it can, execution moves on; if it can't, this step simply produces no result. This can-it-fire kind of check is officially called a «failable expression» — it stars in the next lesson, so jot the name down in your notebook for now.

Now for this lesson's loudest blackboard moment: in Verse, «is the box checked» and «can this wire fire» are two different things. The Branch node you're used to wiring in Blueprint consumes a True/False boolean; Verse's branching pointedly doesn't — it consumes «can this step go through?». false is a value, resting contentedly in that unchecked switch; «can't go through» means the execution wire hit a wall and this step produced no result. The two merely look alike — they're not remotely related. So if you feed the HasKey switch straight into if the way you'd wire a Branch, it lights up red on the spot (the same red you get when a Blueprint Compile fails): the fork wants «did it go through», and you handed it a «checked-or-not value». This is the wall new Verse developers hit hardest, and it's also the language's foundation stone — Lesson 12 builds the whole tower on it; this lesson we just learn to cut a door through the wall.

2. The Question Mark ?: A Revolving Door Between True/False and Success/Failure

Want two paths to fork off this switch's state? Append a question mark ? and turn the «checked-or-not value» into «can this wire fire», right on the spot: HasKey? fires when HasKey is checked (true) and doesn't when it's unchecked (false). Now if (HasKey?): behaves like a perfectly normal Branch and wires up fine. You can read ? as an interrogation — «did you really check the box?» — and failing to answer means the wire doesn't fire. Click «Run next step» and watch the same question mark walk two different wires:

key_gate_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

key_gate_device := class(creative_device):

    # logic slot: do we have the key?
    var HasKey:logic = false

    OnBegin<override>()<suspends>:void =
        if (HasKey?):
            Print("Click — the door swings open!")
        else:
            Print("The door won't budge... go find the key first.")
        set HasKey = true
        if (HasKey?):
            Print("Click — the door swings open!")
Output log

Click «Run next step» to watch the code execute line by line.

The revolving door spins both ways. The reverse direction is written logic{…}: run the «does this wire fire» check inside the braces once — fired yields true, didn't yields false — sealing the outcome of one wire check into a switch value you can store in a variable. On top of that, if in Verse spits out a value of its own, so if (condition) then true else false can also mint a logic value on the spot (a bit like Blueprint's Select node, picking one value to output based on a condition):

coin_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Random }

coin_device := class(creative_device):

    var IsReady:logic = true
    var HasKey:logic = true

    OnBegin<override>()<suspends>:void =
        # logic{}: translate success/failure into a stored true/false
        CoinFlip := logic{GetRandomInt(0, 1) = 1}
        if (CoinFlip?):
            Print("Coin flip: heads!")

        # if is an expression too — it can also mint a logic
        Score := 150
        HasWon := if (Score > 100) then true else false
        if (HasWon?):
            Print("Score threshold met!")

        # Combining logics: give each its own ? first, then chain with and
        if (IsReady? and HasKey?):
            Print("All set — let's go!")

▸ Blueprint translation: the first part stuffs the check «does the random number equal 1» into logic{}, sealing it into the switch CoinFlip, then unpacks it back into a wire with ? — heads means print. The second part uses the pick-a-value-by-condition form (≈ Blueprint's Select node) to compute the HasWon switch, printing once the score qualifies. The last part gives each switch its own ? to become a wire, then chains them with and into «both wires must fire» — like running two checks through an AND node and then into a Branch.

Finally, look at those closing two lines: to combine several conditions, the words and, or, not are all you need (they're Blueprint's AND / OR / NOT nodes). The catch: what they chain are also «can-this-wire-fire» checks, so every logic switch must first grow its own ? to become a wire before and can join them. Write IsReady and HasKey (no question marks) and Compile lights up red.

3. enum: Hanging Nameplates on Your States

New scene. Your map has three phases: waiting in the lobby, battle in progress, podium and curtain call. Track them with an int (0, 1, 2)? Three months from now nobody remembers what 2 meant — «magic numbers» are an ancestral trap. Track them with a string ("Lobby")? One slip of the finger types "Loby", the compiler doesn't bat an eye, and the bug ships. Enter enum — the very Enumeration asset you right-click-create in Blueprint: it builds a dedicated dropdown list whose values can only come from the entries you name, and a misspelled name lights up red at compile time.

phase_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

# enums live at module level, peer to the device class — never inside a function body
game_phase := enum{Lobby, Battle, Podium}

phase_device := class(creative_device):

    # open a slot of the enum type, parked in the lobby at first
    var CurrentPhase:game_phase = game_phase.Lobby

    OnBegin<override>()<suspends>:void =
        set CurrentPhase = game_phase.Battle

        # enums support only = and <> comparisons (both are failable expressions)
        if (CurrentPhase = game_phase.Battle):
            Print("Battle phase — everyone rally up!")
        if (CurrentPhase <> game_phase.Podium):
            Print("Not podium time yet.")

▸ Blueprint translation: the first line is like right-click-creating an Enumeration asset named game_phase with three entries: Lobby, Battle, Podium. The device declares a variable CurrentPhase of that enum type, parked at Lobby by default. On game start (Event BeginPlay), a Set node switches it to Battle, then two Branches ask «is it Battle now?» and «is it not Podium yet?» — note that enums only understand «equals / not-equals» comparisons.

Three points. The definition reads game_phase := enum{Lobby, Battle, Podium}, and it has to sit at the outermost level — peer to your device, the way an Enumeration is a standalone asset you'd never create inside some event graph; write it inside a function or event and Compile bounces it. Naming follows Verse convention: type names in lower_snake_case, entry names in PascalCase. Using a value means the full name game_phase.Lobby (enum-name prefix included), so it can never be confused with a same-named entry in another enum.

enum's powers are deliberately narrow: = and <> can only ask «is it this entry?»; no ordering, no arithmetic; there is no number hiding behind an entry, and it won't auto-convert into an integer or a piece of text; want to Print String it straight to the screen? No dice. To route by the current entry, besides a chain of Branches there's a dedicated case form (like a multi-exit splitter — one lane per entry, exactly one taken at a time) — here's a sneak peek:

phase_label.verse
game_phase := enum{Lobby, Battle, Podium}

# case expression: check members one by one; whichever matches, that branch runs
PhaseLabel(Phase:game_phase):string =
    case (Phase):
        game_phase.Lobby => "Lobby"
        game_phase.Battle => "Battle"
        game_phase.Podium => "Podium"

▸ Blueprint translation: this PhaseLabel is a Blueprint function (takes a game_phase entry, returns a piece of text). The case inside is that multi-exit splitter: an incoming Lobby yields "Lobby", Battle yields "Battle", Podium yields "Podium" — one exit per entry, and only the matching lane runs.

For a default (closed) enum, as long as case spells out every entry, you don't need a «fallback» exit (like Blueprint's Switch on Enum, where the Default pin may stay unwired); better yet, if you later add an entry to the enum but forget to update a case, Compile calls out exactly whom you missed — this safety net is one of enum's most valuable perks. What closed means and whether you can switch to open — the extras page at the end has the full story. First, a small exercise: fill the two key blanks back in:

ready_check_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

# Blank 1: the keyword that defines an enumeration
game_phase := ____{Lobby, Battle, Podium}

ready_check_device := class(creative_device):

    var CurrentPhase:game_phase = game_phase.Lobby
    var IsReady:logic = true

    OnBegin<override>()<suspends>:void =
        set CurrentPhase = game_phase.Battle
        if (CurrentPhase = game_phase.Battle):
            Print("Entering battle phase!")
        # Blank 2: the symbol you add to hand a logic to if
        if (IsReady____):
            Print("Player is ready.")

▸ Blueprint translation: this just glues the two halves together — first build the game_phase enum (blank 1 is the keyword that creates an enum), switch to Battle on game start and Branch on the phase; then that IsReady switch needs the symbol that turns it into a wire (blank 2) before it can feed the if.

4. Choosing Well: When logic, When enum

Two rules of thumb cover ninety percent of everyday cases:

First: the answer is naturally yes/no, and will forever be only those two → use logic. Got the key or not, door open or not, alive or not — all this family. Second: there are three or more states; or only two, but the meaning is «A or B» rather than «yes or no» (say, red team / blue team) → use enum. Team:team = team.Red reads far better than IsRedTeam:logic, and adding a spectator faction later takes just one more member.

There's an advanced reason too: composing state from multiple logics breeds «ghost states». Want a door with three states — open / closed / locked? Model it with two logics, IsOpen plus IsLocked, and you get four combinations — including «open and locked at once», which physically cannot exist yet struts around your code just fine, waiting to ambush you some midnight. Switch to door_state := enum{Open, Closed, Locked} and the illegal state becomes unrepresentable at the type level. «Make illegal states unrepresentable» is the golden rule of type design, and enum is its cheapest implementation.

Scenario Use Why
Got the key? Ready or not? logic Naturally yes/no, capped at two values
Game phase: lobby / battle / podium enum Three or more states; the names document themselves
Faction: red team / blue team enum Meaning is «A or B», not «yes or no» — and easy to extend
Door: open / closed / locked enum Two logics would breed the «open and locked» ghost state

5. Common Pitfalls: Stepped On So You Don't Have To

Almost every pitfall in this lesson grows out of the same crack: «a checked-or-not value ≠ whether the wire fires». Find your own footprints below and get immunized early:

Blueprint Cross-Reference

Every concept in this lesson has a Blueprint counterpart — but the pair that lines up worst, Branch versus if, happens to be the lesson's foundation stone.

In Blueprints In Verse Difference
Create a Boolean in the variables panel (that checkbox) var IsDoorOpen:logic = false Only the name changed: Blueprints call it Boolean, Verse calls it logic, and the values are still just true / false
Branch node: eats a True/False, forks two execution wires if (HasKey?): The biggest mismatch on the page. Verse's if eats «can this go through», and a logic value needs a ? before it may enter
AND / OR / NOT nodes and / or / not What they chain is also «can this go through», so every logic must grow its own ? before being chained
Select node: pick one value to output based on a condition if (Score > 100) then true else false In Verse if is itself an expression — no second node needed for it to hand back a value
A Set node on each lane of a Branch, flipping a Boolean to true / false B := logic{condition} Three steps collapse into one: seal «did it go through» straight into a switch value, without even opening a variable first
Right-click to create an Enumeration asset game_phase := enum{Lobby, Battle, Podium} Even the placement maps: an enum sits at the outermost level, peer to your device class, like a standalone asset — never inside a function body
Switch on Enum node case (Phase): Spell out every entry and the Default pin can stay unwired; add an entry later and forget to update the case, and Compile names the one you missed
The integer index behind each enum member, and its automatic name string Neither exists A Verse enum converts to no int and prints to no screen; for text you write your own case mapping function

The differences concentrate in row two, and their root is in the language core. A Blueprint execution wire has exactly one state — running — so a fork has to be commanded by a boolean handed in from outside, which is why Branch needs a True/False input. In Verse every expression already carries two possible outcomes, went-through and didn't, and the fork is a direct consequence of that outcome rather than an order from elsewhere. In this system logic is just an ordinary data type — like int and string, it exists to be stored, not to command. ? and logic{} are the two revolving doors between the worlds.

The enum rows are a different kind of gap. A Blueprint Enumeration is, underneath, a named integer, so it converts, prints and orders. A Verse enum is none of those things — it is a nameplate that answers exactly one question: is it this entry? What you buy with the amputated powers is case's exhaustiveness check: miss an entry and the compiler names it, instead of a player finding the unhandled branch for you some evening.

6. Level Challenge

Coin and nameplates in hand — clear three mini-levels to prove it. Wrong answers cost nothing; retry as often as you like.

HasKey is a logic variable, and you want the door to open when it's true. Which form both compiles and is the officially recommended idiom?

Which statement about enum is true?

Ok := logic{Score > 100} — when Score is 50, what ends up inside Ok?

Further Reading

Tip · EXTRA

Hand-Rolling ToString for enum: The case Mapping Pattern

enum can't be Printed directly? The community's standard fix is a single case function — with string interpolation support thrown in for free.

Read the extra →

Deep Dive · EXTRA

open vs closed enum: Can You Still Change It After Release?

Can an enum grow new members once your project ships? How version compatibility gets written into the type system — required reading for anything you plan to run long-term.

Read the extra →

Advanced · EXTRA

The Verse Calculus: Why Failure Isn't a Boolean

The theoretical answer to «if won't eat a logic» hides in Epic's ICFP 2023 paper — a glimpse of Verse's linguistic core.

Read the extra →