Overview
By default, every value in Verse is immutable — once set, it never changes. That's great for safety, but games need state that evolves: a counter that ticks up each time a player presses a button, a flag that remembers whether a vault door is already open, a timer that resets every round.
The var keyword in Verse opts a field into mutability. You declare it on a class field, and then anywhere inside that class you can write set MyField = NewValue to update it. This is the foundational pattern behind almost every stateful Verse device you'll ever write.
Reach for mutable variables when you need to:
- Count how many times something has happened (button presses, kills, pickups)
- Remember a boolean flag across multiple event firings (door open/closed, puzzle solved)
- Accumulate a running total that drives a win condition
- Store a reference that may change over the course of a match
API Reference
(API surface could not be resolved for this device.)
Walkthrough
Scenario: A vault room has a pressure plate (modeled here as a Patchwork interactable device). Each time a player interacts with it, a counter increments. After 3 interactions, a creative_prop representing the vault door hides itself (simulating it sliding open), and the counter resets so the puzzle can repeat.
This example demonstrates:
- Declaring a mutable
varfield (PressCount) - Reading it inside a handler
- Writing it with
set - Calling
creative_prop.Hide()andcreative_prop.Show()from the real API - Subscribing to
InteractedWithEventfrom the Patchwork module - Using
Sleepto hold the door open before resetting
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/Patchwork }
using { /Verse.org/Simulation }
# Place this device in your level, then assign the two @editable fields
# in the UEFN Details panel.
vault_door_counter_device := class(creative_device):
# The trigger device that acts as the pressure plate.
@editable
PressurePlate : trigger_device = trigger_device{}
# A creative_prop that represents the vault door mesh.
@editable
VaultDoor : creative_prop = creative_prop{}
# How many interactions are required to open the door.
@editable
RequiredPresses : int = 3
# Mutable counter — starts at zero, increments each interaction.
var PressCount : int = 0
# Mutable flag — prevents re-opening while the door is already open.
var DoorIsOpen : logic = false
OnBegin<override>()<suspends> : void =
# Subscribe the handler to the plate's TriggeredEvent.
PressurePlate.TriggeredEvent.Subscribe(OnPlateInteracted)
# Keep the coroutine alive so subscriptions remain active.
Sleep(Inf)
# Called every time an agent interacts with the pressure plate.
OnPlateInteracted(Agent : ?agent) : void =
# If the door is already open, ignore further presses.
if (DoorIsOpen?):
return
# Increment the press counter.
set PressCount = PressCount + 1
# Check whether we've hit the required number of presses.
if (PressCount >= RequiredPresses):
set DoorIsOpen = true
set PressCount = 0 # Reset for the next cycle.
spawn { OpenThenCloseDoor() }
# Async task: hide the door, wait, then show it again.
OpenThenCloseDoor()<suspends> : void =
VaultDoor.Hide() # Door slides open.
Sleep(5.0) # Hold open for 5 seconds.
VaultDoor.Show() # Door slides closed.
set DoorIsOpen = false # Allow the puzzle to restart.```
### Line-by-line explanation
| Lines | What's happening |
|---|---|
| `@editable PressurePlate` | Exposes the interactable device slot in the UEFN Details panel so you can drag-and-drop the in-world device. |
| `@editable VaultDoor` | Same for the prop that represents the door mesh. |
| `var PressCount : int = 0` | Mutable integer field. The `var` keyword is what makes `set PressCount = ...` legal later. |
| `var DoorIsOpen : logic = false` | Mutable boolean flag. `logic` is Verse's bool type. |
| `PressurePlate.InteractedWithEvent.Subscribe(OnPlateInteracted)` | Wires the Patchwork device's event to our handler method. |
| `Sleep(Inf)` | Keeps `OnBegin` suspended forever so the subscriptions stay alive. |
| `if (DoorIsOpen?):` | The `?` suffix tests whether a `logic` value is `true` in a failure context. |
| `set PressCount = PressCount + 1` | The `set` keyword is **required** to mutate a `var` field — omitting it is a compile error. |
| `spawn { OpenThenCloseDoor() }` | Launches the async door task without blocking the event handler. |
| `VaultDoor.Hide()` / `VaultDoor.Show()` | Real `creative_prop` API calls that toggle mesh visibility and collision. |
## Common patterns
### Pattern 1 — Float accumulator with a timed loop
Track elapsed match time in a mutable float, updated every second. Useful for a custom HUD timer or a time-based difficulty ramp.
```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
match_timer_device := class(creative_device):
# Mutable float accumulates seconds since match start.
var ElapsedSeconds : float = 0.0
# After this many seconds, hide the prop to signal "time's up".
@editable
TimeLimitSeconds : float = 30.0
@editable
TimerProp : creative_prop = creative_prop{}
OnBegin<override>()<suspends> : void =
loop:
Sleep(1.0)
set ElapsedSeconds = ElapsedSeconds + 1.0
if (ElapsedSeconds >= TimeLimitSeconds):
TimerProp.Hide()
break
Pattern 2 — Toggle flag on interaction
A single interactable toggles a prop between visible and hidden each time it's used — like a light switch.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/Patchwork }
using { /Verse.org/Simulation }
light_switch_device := class(creative_device):
@editable
Switch : interactable_device = interactable_device{}
@editable
LightProp : creative_prop = creative_prop{}
# Mutable toggle — true means the light is currently ON (visible).
var LightOn : logic = true
OnBegin<override>()<suspends> : void =
Switch.InteractedWithEvent.Subscribe(OnSwitchUsed)
Sleep(Inf)
OnSwitchUsed(Agent : agent) : void =
if (LightOn?):
LightProp.Hide()
set LightOn = false
else:
LightProp.Show()
set LightOn = true
Pattern 3 — Mutable reference swap
Two props take turns being the "active" target. Each interaction swaps which one is visible, demonstrating that var can hold any type — including object references.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/Patchwork }
using { /Verse.org/Simulation }
target_swap_device := class(creative_device):
@editable
Button : interactable_device = interactable_device{}
@editable
PropA : creative_prop = creative_prop{}
@editable
PropB : creative_prop = creative_prop{}
# Mutable index: 0 = PropA active, 1 = PropB active.
var ActiveIndex : int = 0
OnBegin<override>()<suspends> : void =
# Start with PropA visible, PropB hidden.
PropA.Show()
PropB.Hide()
Button.InteractedWithEvent.Subscribe(OnButtonPressed)
Sleep(Inf)
OnButtonPressed(Agent : agent) : void =
if (ActiveIndex = 0):
PropA.Hide()
PropB.Show()
set ActiveIndex = 1
else:
PropB.Hide()
PropA.Show()
set ActiveIndex = 0
Gotchas
1. You MUST use set to mutate — assignment without it is a compile error
Writing PressCount = PressCount + 1 looks like mutation but Verse treats it as a new immutable binding that shadows the field. The compiler will reject it or silently create a local. Always write set PressCount = PressCount + 1.
2. var fields are per-instance, not global
Each placed copy of your device has its own PressCount. If you place two copies, they count independently. This is usually what you want, but be aware if you're trying to share state across devices — you'll need a separate manager device.
3. Mutating from inside a <transacts> context rolls back on failure
If you call set inside an expression that has the <transacts> effect and that expression fails, the mutation is rolled back. Keep your set statements outside failure-context if branches when you want the write to be permanent, or structure your logic so the failure path is handled before the mutation.
4. logic is not bool — test it with ? in a failure context
To branch on a logic field, write if (MyFlag?): not if (MyFlag = true):. The ? operator is the idiomatic way to test logic values and it only succeeds (continues) when the value is true.
5. Spawning async work from a sync handler
Event handlers like OnPlateInteracted are synchronous — you cannot call Sleep directly inside them. If you need time-based behavior triggered by an event, use spawn { MyAsyncMethod() } to launch a new concurrent task, as shown in the walkthrough.
6. @editable fields must be assigned in the UEFN Details panel
If you forget to drag a device into an @editable slot, it stays as the default empty value and calls on it will silently do nothing (or crash at runtime). Always verify your bindings in the Details panel before testing.