Reference Verse compiles

set-needs-var: Tracking Mutable State in Verse

Every interesting game mechanic needs memory — a door that remembers it was unlocked, a cove that tracks how many players reached the shore, a clifftop beacon that toggles on and off. In Verse, `var` is how you give a device that memory. This article teaches you exactly how to declare mutable variables, update them with `set`, and wire them into real game events on a sun-drenched island.

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

Overview

In Verse, values are immutable by default. If you want a value that can change over time — a score counter, a flag that records whether the vault door is open, a team index — you must declare it with the var keyword. The set expression then updates that variable at runtime.

This matters the moment you build anything stateful:

  • A clifftop lighthouse that toggles its beacon each time a player arrives.
  • A cove gate that only opens after three players have reached the shore.
  • A dock leaderboard that tracks how many fish each player has caught.

Without var and set, your device resets to its initial value every time — making persistent, reactive game logic impossible.

When to reach for it: Any time a value in your device needs to change after OnBegin runs, you need var. If the value never changes, a plain let-style binding (no var) is cleaner and safer.

API Reference

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

Walkthrough

Scenario: A sunny island cove has a pressure plate on the dock. Three players must step onto the plate to raise the harbour gate. A counter tracks arrivals; once it hits three the gate trigger fires and a message is broadcast. We use var to hold the arrival count and set to increment it.

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

# Localised announcement helper
HarbourMessage<localizes>(S : string) : message = "{S}"

# dock_gate_controller — place this Verse device on your island,
# then assign the pressure plate and trigger in the Details panel.
dock_gate_controller := class(creative_device):

    # The pressure plate on the dock planks.
    @editable
    DockPlate : trigger_device = trigger_device{}

    # A trigger wired to the harbour gate's open animation / barrier device.
    @editable
    GateTrigger : trigger_device = trigger_device{}

    # How many players must step on the plate before the gate opens.
    @editable
    RequiredPlayers : int = 3

    # ── mutable state ──────────────────────────────────────────────
    # Tracks how many distinct plate activations have occurred.
    var ArrivalCount : int = 0

    # Tracks whether the gate has already been opened this round.
    var GateOpen : logic = false

    # ── lifecycle ──────────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # Subscribe to the plate's triggered event.
        # trigger_device.TriggeredEvent fires with ?agent each activation.
        DockPlate.TriggeredEvent.Subscribe(OnPlayerArrived)

    # ── event handler ──────────────────────────────────────────────
    OnPlayerArrived(MaybeAgent : ?agent) : void =
        # Gate already open? Nothing more to do.
        if (GateOpen?):
            return

        # Increment the arrival counter.
        set ArrivalCount = ArrivalCount + 1

        Print("Dock arrivals: {ArrivalCount} / {RequiredPlayers}")

        # Once we hit the required number, open the gate.
        if (ArrivalCount >= RequiredPlayers):
            set GateOpen = true
            GateTrigger.Trigger()
            Print("Harbour gate raised! The cove is open.")

Line-by-line explanation:

Lines What's happening
var ArrivalCount : int = 0 Declares a mutable integer field. var is required — without it you cannot use set later.
var GateOpen : logic = false A boolean flag. logic is Verse's bool type; false / true are its values.
DockPlate.TriggeredEvent.Subscribe(OnPlayerArrived) Wires the plate's event to our handler. Every step on the plate calls OnPlayerArrived.
if (GateOpen?): The ? suffix is the succeeds-if-true decider for logic. If already open, we bail early.
set ArrivalCount = ArrivalCount + 1 set is the only way to mutate a var. Forgetting set is a compile error.
if (ArrivalCount >= RequiredPlayers): Comparison works on int directly — no casting needed.
GateTrigger.Trigger() Fires the gate trigger, which you wire in UEFN to whatever opens the barrier.

Common patterns

Pattern 1 — Float var: tracking a rising tide timer

A clifftop alarm sounds when a float accumulator crosses a threshold — useful for timed events like a rising-water mechanic.

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

clifftop_tide_alarm := class(creative_device):

    @editable
    AlarmTrigger : trigger_device = trigger_device{}

    # Seconds of "danger" accumulated before the alarm fires.
    @editable
    DangerThreshold : float = 10.0

    # Mutable float accumulator — starts at zero each round.
    var DangerSeconds : float = 0.0

    var AlarmFired : logic = false

    OnBegin<override>()<suspends> : void =
        # Tick every second, accumulating danger time.
        loop:
            Sleep(1.0)
            if (not AlarmFired?):
                set DangerSeconds = DangerSeconds + 1.0
                Print("Danger level: {DangerSeconds}s")
                if (DangerSeconds >= DangerThreshold):
                    set AlarmFired = true
                    AlarmTrigger.Trigger()
                    Print("Clifftop alarm! Evacuate the cove!")

Key point: var DangerSeconds : float = 0.0 — you must write the explicit type annotation. var X := 0.0 is not valid Verse syntax; var always needs : Type.


Pattern 2 — Optional var: remembering the last player to reach the shore

Store the most recent agent who triggered the dock plate so you can award them a prize if they were first.

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

shore_first_arrival := class(creative_device):

    @editable
    ShorePlate : trigger_device = trigger_device{}

    @editable
    PrizeGranter : item_granter_device = item_granter_device{}

    # Holds the first agent to reach the shore, or false (no one yet).
    var FirstArrival : ?agent = false

    OnBegin<override>()<suspends> : void =
        ShorePlate.TriggeredEvent.Subscribe(OnShoreReached)

    OnShoreReached(MaybeAgent : ?agent) : void =
        # Only award the prize if no one has arrived yet.
        if (FirstArrival = false):
            if (A := MaybeAgent?):
                # Record this agent as the first to reach the shore.
                set FirstArrival = option{A}
                PrizeGranter.GrantItem(A)
                Print("First to the shore! Prize granted.")

Key point: var FirstArrival : ?agent = false uses false as the "none" value for an optional. Unwrap with if (A := MaybeAgent?) before passing to APIs that need a concrete agent.


Pattern 3 — Resetting a var: round-restart on the dock

At the end of each round, reset all mutable state so the next round starts fresh.

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

dock_round_manager := class(creative_device):

    @editable
    RoundEndTrigger : trigger_device = trigger_device{}

    @editable
    DockPlate : trigger_device = trigger_device{}

    @editable
    GateTrigger : trigger_device = trigger_device{}

    var RoundNumber : int = 0
    var ArrivalCount : int = 0
    var GateOpen : logic = false

    OnBegin<override>()<suspends> : void =
        DockPlate.TriggeredEvent.Subscribe(OnArrival)
        RoundEndTrigger.TriggeredEvent.Subscribe(OnRoundEnd)

    OnArrival(MaybeAgent : ?agent) : void =
        if (not GateOpen?):
            set ArrivalCount = ArrivalCount + 1
            if (ArrivalCount >= 3):
                set GateOpen = true
                GateTrigger.Trigger()

    OnRoundEnd(MaybeAgent : ?agent) : void =
        # Reset all vars — the next round starts clean.
        set RoundNumber = RoundNumber + 1
        set ArrivalCount = 0
        set GateOpen = false
        Print("Round {RoundNumber} starting. Dock gate reset.")

Key point: set works on any var field from any method in the same class. Resetting vars in a round-end handler is the standard pattern for reusable game loops.

Gotchas

1. var requires an explicit type annotation

This fails:

var Count := 0   # ✗ compile error

This works:

var Count : int = 0   # ✓

Verse does not infer types for var declarations.

2. Forgetting set is a compile error

Writing ArrivalCount = ArrivalCount + 1 without set tries to create a new binding, not mutate the field — the compiler rejects it. Always write set ArrivalCount = ....

3. int and float do not auto-convert

var Seconds : float = 0.0 cannot be compared with an int literal directly in some contexts. Keep your types consistent: use 0.0 for floats and 0 for ints throughout.

4. logic uses ? as a decider, not = true

To branch on a logic var, write if (GateOpen?)not if (GateOpen = true). The latter is an assignment attempt and will not compile as a condition.

5. var fields on a creative_device are per-instance

Each placed copy of your device has its own independent var fields. Two dock_gate_controller devices on the same island do not share ArrivalCount — which is usually what you want, but remember it when debugging multi-device setups.

6. var cannot be @editable

You cannot expose a var field to the UEFN Details panel with @editable. Editable fields are always immutable configuration values. Use a plain (non-var) field for designer-tunable parameters and a separate var for runtime state.

7. Localized text for Print-style messages

If you ever pass text to an API that expects message (not Print), raw strings don't work. Declare a localizes helper:

MyMsg<localizes>(S : string) : message = "{S}"

Then pass MyMsg("Hello cove!"). Print accepts plain strings, but device APIs like notification systems expect message.

Build your own lesson with set_needs_var

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 →