Overview
In Verse, all values are immutable by default. If you want a value that can change after it is first set — a kill counter, a flag that tracks whether a vault is open, a reference to the current target prop — you must declare it with the var keyword. Changing that value later requires the set keyword inside a context that carries the <transacts> effect (which includes OnBegin and any function you mark <transacts>).
Reach for var + set whenever you need to:
- Track a counter that increments each round
- Remember whether a one-shot event has already fired
- Swap which prop or device is currently "active"
- Accumulate a running total (score, damage dealt, items collected)
This article covers the core language feature, not a named UEFN device — var/set is the foundation every device-based script is built on.
API Reference
(API surface could not be resolved for this device.)
Walkthrough
Scenario: A survival arena tracks how many waves the players have survived. Each time a trigger fires (representing a wave ending), the wave counter increments. After three waves the vault door prop is hidden (representing the reward room opening) and the counter resets. A logic flag prevents the reward from triggering twice.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# wave_tracker — place this Verse device in your UEFN level.
# Wire WaveEndTrigger to a trigger_device that fires when each wave ends.
# Wire VaultDoor to a creative_prop that acts as the vault door.
wave_tracker := class(creative_device):
# The trigger a game-manager device signals at the end of each wave.
@editable
WaveEndTrigger : trigger_device = trigger_device{}
# The prop that blocks the reward room — we Hide() it when waves are done.
@editable
VaultDoor : creative_prop = creative_prop{}
# --- mutable state ---
# How many waves have been survived so far.
var WaveCount : int = 0
# Has the vault already been opened? Prevents double-triggering.
var VaultOpened : logic = false
# Total waves required before the vault opens.
WavesRequired : int = 3
OnBegin<override>()<suspends> : void =
# Subscribe to the trigger so OnWaveEnded runs every time it fires.
WaveEndTrigger.TriggeredEvent.Subscribe(OnWaveEnded)
Print("Wave tracker ready. Survive {WavesRequired} waves to open the vault.")
# Called each time WaveEndTrigger fires.
# The event passes ?agent — we accept it but don't need it here.
OnWaveEnded(Agent : ?agent) : void =
# Guard: do nothing if the vault is already open.
if (VaultOpened?):
return
# Increment the wave counter using set.
set WaveCount = WaveCount + 1
Print("Wave {WaveCount} of {WavesRequired} survived!")
# Check whether enough waves have passed.
if (WaveCount >= WavesRequired):
# Mark the vault as opened so this branch never runs again.
set VaultOpened = true
# Hide the vault door prop — the reward room is now accessible.
VaultDoor.Hide()
Print("Vault opened! Well done.")
Line-by-line explanation
| Lines | What's happening |
|---|---|
@editable WaveEndTrigger |
Exposes the trigger reference to the UEFN Details panel so you can wire it up without touching code. |
@editable VaultDoor |
Same for the prop — assign your vault door mesh here. |
var WaveCount : int = 0 |
Declares a mutable integer. Without var this would be a constant. |
var VaultOpened : logic = false |
A boolean flag. logic is Verse's bool type; false means the vault is shut. |
WavesRequired : int = 3 |
No var — this is a constant. It can never be changed at runtime. |
WaveEndTrigger.TriggeredEvent.Subscribe(OnWaveEnded) |
Hooks the handler. Every trigger fire calls OnWaveEnded. |
if (VaultOpened?): |
The ? suffix tests whether a logic value is true (failable context). |
set WaveCount = WaveCount + 1 |
The only way to mutate a var field. Omitting set is a compile error. |
set VaultOpened = true |
Flips the flag so the wave-end handler becomes a no-op from now on. |
VaultDoor.Hide() |
Calls the real creative_prop API to remove the door from the world. |
Common patterns
Pattern 1 — Toggling a prop between visible and hidden
A var logic field acts as a toggle switch. Each button press flips the state and calls the matching creative_prop method.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# prop_toggle_device — attach to a button_device and a creative_prop.
# Each button press shows or hides the prop.
prop_toggle_device := class(creative_device):
@editable
ToggleButton : button_device = button_device{}
@editable
TargetProp : creative_prop = creative_prop{}
# Tracks whether the prop is currently visible.
var PropVisible : logic = true
OnBegin<override>()<suspends> : void =
ToggleButton.InteractedWithEvent.Subscribe(OnButtonPressed)
OnButtonPressed(Agent : agent) : void =
if (PropVisible?):
# Prop is showing — hide it.
TargetProp.Hide()
set PropVisible = false
Print("Prop hidden.")
else:
# Prop is hidden — show it.
TargetProp.Show()
set PropVisible = true
Print("Prop shown.")
Pattern 2 — Clamped damage accumulator
A var float accumulates incoming damage. When it exceeds a threshold the prop is destroyed. The value is clamped so it never goes negative.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# prop_health_device — simulates a breakable prop with a health pool.
# Wire DamageSource to a trigger_device that fires when the prop takes a hit.
prop_health_device := class(creative_device):
@editable
DamageSource : trigger_device = trigger_device{}
@editable
BreakableProp : creative_prop = creative_prop{}
# How much health the prop starts with.
MaxHealth : float = 100.0
# Damage dealt per trigger event.
DamagePerHit : float = 34.0
# Current health — mutable so it decreases on each hit.
var CurrentHealth : float = 100.0
OnBegin<override>()<suspends> : void =
set CurrentHealth = MaxHealth # initialise from the constant
DamageSource.TriggeredEvent.Subscribe(OnHit)
Print("Prop health initialised: {CurrentHealth}")
OnHit(Agent : ?agent) : void =
# Subtract damage, clamp to zero so health never goes negative.
var NewHealth : float = CurrentHealth - DamagePerHit
if (NewHealth < 0.0):
set NewHealth = 0.0
set CurrentHealth = NewHealth
Print("Prop health: {CurrentHealth}")
if (CurrentHealth <= 0.0):
BreakableProp.Dispose()
Print("Prop destroyed!")
Pattern 3 — Round-robin prop selector
A var int index cycles through an array of props, showing the active one and hiding the rest. This is the classic "rotating objective" pattern.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# prop_rotator_device — cycles through up to four props one at a time.
# Wire NextButton to a button_device. Each press advances to the next prop.
prop_rotator_device := class(creative_device):
@editable
NextButton : button_device = button_device{}
# Fill these in the Details panel with your placed creative_prop references.
@editable
PropA : creative_prop = creative_prop{}
@editable
PropB : creative_prop = creative_prop{}
@editable
PropC : creative_prop = creative_prop{}
# Index of the currently visible prop (0, 1, or 2).
var ActiveIndex : int = 0
OnBegin<override>()<suspends> : void =
# Start with only PropA visible.
PropB.Hide()
PropC.Hide()
NextButton.InteractedWithEvent.Subscribe(OnNext)
Print("Prop rotator ready. Active index: {ActiveIndex}")
OnNext(Agent : agent) : void =
# Hide all props first.
PropA.Hide()
PropB.Hide()
PropC.Hide()
# Advance the index, wrapping around at 3.
set ActiveIndex = Mod(ActiveIndex + 1, 3)
# Show only the newly active prop.
if (ActiveIndex = 0):
PropA.Show()
else if (ActiveIndex = 1):
PropB.Show()
else:
PropC.Show()
Print("Active prop index: {ActiveIndex}")
Gotchas
1. set is mandatory — forgetting it is a compile error
Writing WaveCount = WaveCount + 1 without set looks like an assignment but Verse treats it as a comparison (failable equality check). You will get a confusing compile error. Always write set WaveCount = WaveCount + 1.
2. var fields require the <transacts> effect to write
The <transacts> effect is required whenever you write to a var field. OnBegin and most event handlers already carry it implicitly. If you write a helper function that mutates a var, mark it <transacts>: MyHelper()<transacts>:void = .... The compiler will tell you if you miss this.
3. int and float do not auto-convert
Verse is strict about numeric types. var MyFloat : float = 0 is a compile error — you must write 0.0. Similarly, you cannot add an int to a float directly; cast explicitly or keep types consistent throughout.
4. logic is not bool — test it with ? in failable contexts
To branch on a logic value, use if (MyFlag?): not if (MyFlag = true): (though the latter also works). The ? suffix is the idiomatic Verse way and reads more cleanly.
5. var fields on a class are mutable regardless of how you hold the reference
If you store a class instance in a non-var variable, you can still mutate its var fields. Immutability of the variable only means you cannot point it at a different object — the object's own var fields remain writable.
6. Local var inside a function also needs set
Local mutable variables follow the same rule: var Count : int = 0 then later set Count = Count + 1. This applies inside loops too — a common place to forget set when porting logic from other languages.
7. Disposing a creative_prop is permanent
Once you call Dispose() on a prop it is gone for the session. If you only want to make it invisible and intangible, use Hide() / Show() instead. Check IsValid() (failable) before calling methods on a prop that might have been disposed elsewhere.