Reference Devices compiles

mutable-var-set-v6: Tracking Game State That Changes

In Fortnite creative games, almost everything changes: health drops, scores climb, doors open and close. Verse's `var` keyword lets you declare mutable fields inside your device class so that state can evolve while the match runs. This article teaches you how to declare, read, and update mutable variables inside a `creative_device`, wiring them to real device events like `InteractedWithEvent` and timed loops with `Sleep`.

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

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 var field (PressCount)
  • Reading it inside a handler
  • Writing it with set
  • Calling creative_prop.Hide() and creative_prop.Show() from the real API
  • Subscribing to InteractedWithEvent from the Patchwork module
  • Using Sleep to 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.

Build your own lesson with mutable_var_set_v6

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 →