Reference Devices compiles

mutable var & set: Stateful Game Logic in Verse

Every interesting game needs state that changes: a door that opens once, a score that climbs, a wave counter that ticks up. In Verse, `var` declares a mutable field and `set` is the only way to change it at runtime. Master these two keywords and you can build virtually any stateful game mechanic.

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

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.

Build your own lesson with mutable_var_set

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 →