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 holdsMyValue
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
?agentand 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
varfield of type?agent - Unwrapping
?agentfrom an event withif (A := Agent?) - Using
option{...}to wrap a failable expression - Checking an optional with
ifbefore 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.