Reference Verse compiles

option: Safe Values in Verse

In Fortnite game logic, things are often uncertain: did a player actually trigger the plate, or was it empty? Is there really an agent behind that event? Verse's `option` type is the language's answer — a container that holds either a value (`?value`) or nothing (`false`), forcing you to handle both cases before your code can proceed. Master `option` and you'll write safer, crash-free game logic every time.

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

Overview

An option in Verse is a typed container that either holds a value or is empty. It is written as ?T for a type that might have a T, and its two states are:

  • false — the option is empty (no value present)
  • ?MyValue — the option holds MyValue

You encounter option constantly in UEFN because many APIs return optional values — for example, events that carry ?agent (an agent that might or might not be present), or lookups that might fail. Trying to use an optional value without unwrapping it first is a compile error, so Verse forces you to be explicit about the "nothing" case.

When to reach for option:

  • An event handler receives ?agent and you need the actual agent
  • You want to store a value that might not be set yet (e.g., "the last player who stepped on a plate")
  • You want to convert a failable (<decides>) expression into a safe value
  • You want to pass a value that is intentionally absent in some code paths

API Reference

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

Walkthrough

Scenario: A pressure plate unlocks a vault door prop. When a player steps on the plate, we record which player triggered it (storing them as ?agent), then show the vault door. When the plate is released, we hide the door again and clear the stored player. A HUD message shows who opened the vault.

This example demonstrates:

  • Declaring a var field of type ?agent
  • Unwrapping ?agent from an event with if (A := Agent?)
  • Using option{...} to wrap a failable expression
  • Checking an optional with if before acting on it
  • Clearing an optional back to false
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# vault_controller — tracks who opened the vault and shows/hides the door prop.
vault_controller := class(creative_device):

    # The pressure plate the player steps on.
    @editable
    Plate : trigger_device = trigger_device{}

    # The vault door prop to show/hide.
    @editable
    VaultDoor : creative_prop = creative_prop{}

    # The HUD message device to display who opened the vault.
    @editable
    HUD : hud_message_device = hud_message_device{}

    # Stores the agent who is currently holding the plate open.
    # Starts as false (no one on the plate).
    var CurrentOpener : ?agent = false

    # Localized helper so we can pass a message to the HUD device.
    VaultOpenedBy<localizes>(Name : string) : message = "Vault opened by: {Name}"
    VaultClosed<localizes>() : message = "Vault is closed."

    OnBegin<override>()<suspends> : void =
        # Hide the vault door at game start.
        VaultDoor.Hide()

        # Subscribe to the plate's triggered event.
        Plate.TriggeredEvent.Subscribe(OnPlateTriggered)

        # Keep the coroutine alive so subscriptions remain active.
        loop:
            Sleep(10.0)

    # Called when a player steps onto the plate.
    # TriggeredEvent fires with ?agent — we must unwrap it.
    OnPlateTriggered(Agent : ?agent) : void =
        if (A := Agent?):
            # Store the opener as a non-empty option.
            set CurrentOpener = option{A}
            VaultDoor.Show()
            # Show who opened it. GetAgentName is illustrative;
            # in practice use your player-name API or a fixed label.
            HUD.SetText(VaultOpenedBy("a player"))
            HUD.Show()
        else:
            # No agent — environmental trigger, ignore.
            Print("Plate triggered with no agent.")```

**Line-by-line breakdown:**

| Line | What it teaches |
|---|---|
| `var CurrentOpener : ?agent = false` | Declares a mutable optional agent, initialized to empty (`false`) |
| `OnPlateTriggered(Agent : ?agent)` | Event handlers for `listenable(?agent)` receive `?agent`  you must unwrap |
| `if (A := Agent?)` | The `?` postfix operator unwraps the option inside an `if`; fails (takes else branch) if empty |
| `set CurrentOpener = option{A}` | `option{...}` wraps a plain value back into an option |
| `if (Opener := CurrentOpener?)` | Same pattern to read our stored optional before using it |
| `set CurrentOpener = false` | Resetting an optional to empty is just assigning `false` |

## Common patterns

### Pattern 1 — Storing an optional result and checking it later

Sometimes you want to remember "the first player to arrive" and only act when someone is actually stored. This pattern shows declaring, setting, and reading a `?agent` field across multiple functions.

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

# first_arrival_tracker — records the first player to step on a zone.
first_arrival_tracker := class(creative_device):

    @editable
    ArrivalZone : trigger_device = trigger_device{}

    @editable
    RewardGranter : item_granter_device = item_granter_device{}

    # Holds the first player who arrived; false until someone steps on the zone.
    var FirstArrival : ?agent = false

    OnBegin<override>()<suspends> : void =
        ArrivalZone.TriggeredEvent.Subscribe(OnArrival)
        loop:
            Sleep(10.0)

    OnArrival(MaybeAgent : ?agent) : void =
        # Only act if no one has arrived yet (FirstArrival is false).
        if (FirstArrival?):
            # Already have a first arrival — do nothing.
            return

        # Unwrap the incoming agent.
        if (A := MaybeAgent?):
            # Store as the first arrival.
            set FirstArrival = option{A}
            # Grant a reward to the first player.
            RewardGranter.GrantItem(A)
            Print("First arrival recorded and rewarded!")

Pattern 2 — Converting a failable expression into an option with option{...}

The option{...} block turns any <decides> expression into a ?T. This is useful when you want to attempt something that might fail and store the result for later inspection rather than branching immediately.

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

# health_gate — only opens the door if the triggering player has full health.
health_gate := class(creative_device):

    @editable
    CheckTrigger : trigger_device = trigger_device{}

    @editable
    GateProp : creative_prop = creative_prop{}

    FullHealthThreshold : float = 100.0

    OnBegin<override>()<suspends> : void =
        GateProp.Hide()
        CheckTrigger.TriggeredEvent.Subscribe(OnCheck)
        loop:
            Sleep(10.0)

    OnCheck(MaybeAgent : ?agent) : void =
        # Unwrap the agent first.
        if (A := MaybeAgent?):
            # Try to get the fort_character — this is a <decides> expression,
            # so wrap it in option{} to get a ?fort_character.
            MaybeChar := option{A.GetFortCharacter[]}

            # Now unwrap the character option.
            if (Char := MaybeChar?):
                Health := Char.GetHealth()
                if (Health >= FullHealthThreshold):
                    # Player is at full health — open the gate.
                    GateProp.Show()
                    Print("Gate opened for full-health player.")
                else:
                    Print("Player health too low — gate stays closed.")
            else:
                Print("Could not get character from agent.")

Pattern 3 — Chained optional unwrapping (nested if guards)

Real game logic often chains multiple optional checks. Each if guard narrows the type further. This pattern shows a sequence of unwraps that must all succeed before an action fires.

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

# proximity_reward — grants an item only if the agent is close enough to a prop.
proximity_reward := class(creative_device):

    @editable
    ActivateTrigger : trigger_device = trigger_device{}

    @editable
    TargetProp : creative_prop = creative_prop{}

    @editable
    Granter : item_granter_device = item_granter_device{}

    # Maximum allowed distance in centimeters.
    MaxDistance : float = 500.0

    OnBegin<override>()<suspends> : void =
        ActivateTrigger.TriggeredEvent.Subscribe(OnActivate)
        loop:
            Sleep(10.0)

    OnActivate(MaybeAgent : ?agent) : void =
        # Chain of optional unwraps — all must succeed.
        if (A := MaybeAgent?):
            if (Char := option{A.GetFortCharacter[]}?):
                # Get positions.
                PlayerPos := Char.GetTransform().Translation
                PropPos := TargetProp.GetTransform().Translation

                # Compute distance manually (no built-in Distance in this scope).
                Diff := vector3:
                    X := PlayerPos.X - PropPos.X
                    Y := PlayerPos.Y - PropPos.Y
                    Z := PlayerPos.Z - PropPos.Z
                DistSq := Diff.X * Diff.X + Diff.Y * Diff.Y + Diff.Z * Diff.Z
                MaxSq := MaxDistance * MaxDistance

                if (DistSq <= MaxSq):
                    Granter.GrantItem(A)
                    Print("Close enough — item granted!")
                else:
                    Print("Too far away.")

Gotchas

1. false means empty, not false the boolean

In Verse, false is overloaded: for a logic variable it means the boolean false, but for an option variable it means "no value". Writing var MyOpt : ?agent = false is correct and idiomatic — it does NOT mean the agent is somehow "false". New Verse developers often confuse this with boolean logic.

2. You CANNOT use an optional value directly — you must unwrap it

This does NOT compile:

# WRONG — cannot call methods on ?agent directly
CurrentOpener.GetFortCharacter[]  # compile error

You must always unwrap first:

if (A := CurrentOpener?):
    A.GetFortCharacter[]

3. The ? postfix operator only works inside a <decides> context

The ? postfix unwrap is itself a <decides> expression — it fails if the option is empty. That means it can only appear inside an if, and, or, not, or another <decides> context. Calling MyOpt? at the top level of a function body is a compile error.

4. option{...} wraps a <decides> expression — it does NOT wrap a plain value directly in most contexts

To store a plain agent value A as ?agent, write option{A} — this works because a plain value trivially succeeds as a <decides> expression. But option{SomeFunctionThatReturnsVoid[]} will not give you a useful ?void in most game contexts — use option{} only with expressions that return a meaningful type.

5. Event handlers that receive ?agent — always unwrap before use

Events like trigger_device.TriggeredEvent are typed listenable(?agent). Your handler signature MUST be (Agent : ?agent) : void and you MUST unwrap with if (A := Agent?) before calling any agent methods. Skipping this step is the single most common runtime-logic bug in Verse event code.

6. Resetting an optional is just assigning false

To clear a stored optional, simply set MyOpt = false. There is no .clear() method or none keyword — false is the canonical empty option value in Verse.

Guides & scripts that use option

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

Build your own lesson with option

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 →