Reference Verse compiles

Boolean Logic in Verse: Controlling Game State with true and false

Every game is built on decisions: is the door locked? Did the player earn the reward? Should the barrier block this team? In Verse, all of these yes/no questions are answered with the `logic` type — the Boolean heart of your creative device code. This article teaches you how to declare, test, combine, and act on `logic` values using real UEFN devices like `barrier_device`, so your islands respond intelligently to player actions.

Updated Examples verified on the live UEFN compiler
Watch the Knotboolean_logic in ~90 seconds.

Overview

Verse uses the logic type to represent Boolean values. A logic variable can hold exactly one of two values: true or false. This is the foundation of every conditional branch, every gate, every "has the player done X yet?" check in your island.

You reach for logic whenever you need to:

  • Track a game-state flag ("has the vault been opened?", "is the round active?")
  • Combine multiple conditions before allowing an action (and, or, not)
  • Drive device behaviour — enabling or disabling a barrier_device, branching on team membership, etc.
  • Avoid running the same setup twice with a simple guard variable

The logic type is not a number. You cannot add or subtract it. You test it with the ? (query) operator inside a failure context like if, or with and/or/not expressions. The result of a query is either success (the expression continues) or failure (the branch is skipped).

API Reference

(API surface could not be resolved for this device.)

Walkthrough

Scenario: A two-team box-fight arena. Team 1 stands on a pressure plate. When they do, a central barrier drops so both teams can fight. A logic flag prevents the barrier from being disabled a second time if the event fires again.

The barrier_device is the real UEFN device we control here. We use Enable() and Disable() — the two core methods on barrier_device — driven entirely by logic state.

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

# A two-team arena: stepping on the plate drops the central barrier.
# A logic flag ensures we only drop it once per round.
box_fight_manager := class<concrete>(creative_device):

    # Wire these up in the UEFN editor via the @editable properties panel.
    @editable
    CentralBarrier : barrier_device = barrier_device{}

    @editable
    StartPlate : trigger_device = trigger_device{}

    # Boolean flag: has the barrier already been dropped this round?
    var BarrierDropped : logic = false

    OnBegin<override>()<suspends> : void =
        # Subscribe: whenever a player steps on the plate, call OnPlateTriggered.
        StartPlate.TriggeredEvent.Subscribe(OnPlateTriggered)

        # Make sure the barrier starts enabled (blocking movement).
        CentralBarrier.Enable()

    # Handler receives an optional agent — the player who triggered the plate.
    OnPlateTriggered(Agent : ?agent) : void =
        # Guard: only act if the barrier has NOT been dropped yet.
        if (not BarrierDropped?):
            # Mark it dropped so this branch never runs again this round.
            set BarrierDropped = true
            # Drop the barrier — both teams can now engage.
            CentralBarrier.Disable()

    # Call this to reset the arena for a new round.
    ResetArena() : void =
        set BarrierDropped = false
        CentralBarrier.Enable()

Line-by-line explanation:

Line What it does
var BarrierDropped : logic = false Declares a mutable Boolean flag, starting as false. The var keyword makes it reassignable.
StartPlate.TriggeredEvent.Subscribe(OnPlateTriggered) Wires the plate's event to our handler at runtime.
CentralBarrier.Enable() Ensures the barrier is solid at round start.
OnPlateTriggered(Agent : ?agent) Event handlers for trigger_device receive ?agent (an optional agent). We don't need to unwrap it here because our logic doesn't use the player identity.
if (not BarrierDropped?): The ? operator queries the logic value. not inverts the result. If BarrierDropped is false, BarrierDropped? fails, not turns that into success, so the if body runs.
set BarrierDropped = true Flips the flag so subsequent plate triggers do nothing.
CentralBarrier.Disable() The real device call — drops the barrier.
ResetArena() Resets both the flag and the physical barrier for the next round.

Common patterns

Pattern 1 — Combining conditions with and before acting

Two barriers must BOTH be active before we allow a reset. We combine two logic variables with and.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Only re-enable the exit barrier when BOTH checkpoints are cleared.
dual_checkpoint_manager := class<concrete>(creative_device):

    @editable
    ExitBarrier : barrier_device = barrier_device{}

    @editable
    CheckpointA : trigger_device = trigger_device{}

    @editable
    CheckpointB : trigger_device = trigger_device{}

    var CheckpointACleared : logic = false
    var CheckpointBCleared : logic = false

    OnBegin<override>()<suspends> : void =
        ExitBarrier.Enable()
        CheckpointA.TriggeredEvent.Subscribe(OnCheckpointA)
        CheckpointB.TriggeredEvent.Subscribe(OnCheckpointB)

    OnCheckpointA(Agent : ?agent) : void =
        set CheckpointACleared = true
        MaybeOpenExit()

    OnCheckpointB(Agent : ?agent) : void =
        set CheckpointBCleared = true
        MaybeOpenExit()

    # Only open the exit when BOTH flags are true.
    MaybeOpenExit() : void =
        if (CheckpointACleared? and CheckpointBCleared?):
            ExitBarrier.Disable()

Key idea: CheckpointACleared? and CheckpointBCleared? — both query expressions must succeed for the if body to run. Short-circuit evaluation means if the first fails, the second is never tested.


Pattern 2 — Using logic{} to convert a failable expression into a value

Sometimes you want to store the result of a test as a logic value rather than branching immediately. logic{} wraps a failable expression and produces true on success or false on failure.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Decide at setup time whether the barrier should start open or closed,
# based on a configurable threshold.
vault_gate_manager := class<concrete>(creative_device):

    @editable
    VaultBarrier : barrier_device = barrier_device{}

    # Set this in the editor: 0 = always closed, 1 = always open.
    @editable
    var OpenOnStart : logic = false

    OnBegin<override>()<suspends> : void =
        # Use the stored logic value directly to decide initial state.
        if (OpenOnStart?):
            VaultBarrier.Disable()   # Start open
        else:
            VaultBarrier.Enable()    # Start closed

    # Toggle: flip the current state and apply it to the barrier.
    ToggleVault(CurrentlyOpen : logic) : void =
        # logic{} converts the query result to a logic value.
        var NextState : logic = logic{not CurrentlyOpen?}
        if (NextState?):
            VaultBarrier.Disable()
        else:
            VaultBarrier.Enable()

Key idea: logic{not CurrentlyOpen?} — the logic{} expression evaluates the failable not CurrentlyOpen? and stores true or false as a concrete logic value you can pass around or store in a variable.


Pattern 3 — Per-agent ignore list driven by a logic flag

barrier_device has AddToIgnoreList and RemoveFromIgnoreList. We use a logic flag to track whether a specific player is currently on the VIP pass list, and toggle them in and out.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# A VIP barrier: certain players can walk through; others are blocked.
# A logic flag per-session tracks whether VIP access is active.
vip_barrier_manager := class<concrete>(creative_device):

    @editable
    VIPBarrier : barrier_device = barrier_device{}

    @editable
    VIPPlate : trigger_device = trigger_device{}

    @editable
    RevokeButton : button_device = button_device{}

    var VIPAccessGranted : logic = false

    OnBegin<override>()<suspends> : void =
        VIPBarrier.Enable()
        VIPPlate.TriggeredEvent.Subscribe(OnVIPPlateTriggered)
        RevokeButton.InteractedWithEvent.Subscribe(OnRevokePressed)

    OnVIPPlateTriggered(MaybeAgent : ?agent) : void =
        # Unwrap the optional agent before using them.
        if (A := MaybeAgent?):
            if (not VIPAccessGranted?):
                set VIPAccessGranted = true
                # This agent can now pass through the barrier.
                VIPBarrier.AddToIgnoreList(A)

    OnRevokePressed(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            if (VIPAccessGranted?):
                set VIPAccessGranted = false
                VIPBarrier.RemoveFromIgnoreList(A)

Key idea: We unwrap ?agent with if (A := MaybeAgent?): before passing the agent to AddToIgnoreList / RemoveFromIgnoreList. The logic flag prevents double-adding the same agent.


Gotchas

1. logic is NOT a number — no arithmetic

# WRONG — this does not compile:
var Count : int = 0
if (Count + IsReady):   # type error: int and logic are incompatible

You cannot add, subtract, or compare logic with numbers. Test it with ? inside a failure context only.


2. The ? query operator only works inside a failure context

? is a failable expression. It must appear inside if, and/or, logic{}, or another failure context. Calling MyFlag? as a standalone statement is a compile error.

# WRONG:
MyFlag?   # standalone query — not in a failure context, won't compile

# RIGHT:
if (MyFlag?): DoSomething()

3. Mutable logic variables require var and set

Without var, the variable is immutable. Without set, the assignment fails to compile.

# WRONG — immutable, cannot reassign:
BarrierDropped : logic = false
BarrierDropped = true   # compile error

# RIGHT:
var BarrierDropped : logic = false
set BarrierDropped = true

4. not inverts a failable expression, not a value

not wraps a failable expression and inverts its success/failure. It does NOT take a logic value directly — it takes a failable expression that produces success or failure.

# Correct usage — BarrierDropped? is a failable expression:
if (not BarrierDropped?): DoSetup()

# Also correct — logic{} converts to a stored value first:
var Inverted : logic = logic{not BarrierDropped?}

5. barrier_device ignore list does NOT affect bullets

The Epic docs explicitly note: AddToIgnoreList lets an agent walk through the barrier, but bullets still collide. If you need bullet pass-through, you must Disable the barrier entirely rather than relying on the ignore list.

Guides & scripts that use boolean_logic

Step-by-step tutorials that put this object to work.

Build your own lesson with boolean_logic

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →