Reference Verse compiles

constants in Verse: Timed Treasure Runs on the Pirate Cove

Constants are the bedrock of clean Verse code — values you name once and trust forever. In this article you'll learn exactly what a constant is, how Verse's type-inference shorthand works, and why reaching for a named constant instead of a magic number makes your island logic readable and bug-resistant. Every example is grounded in a bright, cel-shaded pirate cove where players race to loot a treasure chest before the timer runs out.

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

Overview

A constant in Verse is a binding between an identifier and a value that cannot be reassigned after it is initialized. Unlike a variable (declared with var), a constant has no set expression — the compiler rejects any attempt to change it.

Reach for constants when:

  • A value is fixed for the entire session (max players, a door's unlock score, a tide-timer duration).
  • You want a single source of truth so changing one number updates every place it is used.
  • You are naming a magic number to make code self-documenting (MaxWaves := 5 beats a bare 5 scattered across your file).

Constants are the backbone of readable, maintainable Verse. Every @editable field on a creative_device is also a constant — the editor sets it once before OnBegin runs and it never changes.

API Reference

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

Walkthrough

Scenario: A clifftop cove island. When the round starts a countdown timer ticks down from a constant number of seconds. If a player reaches the dock trigger before time runs out, a prop-mover swings the harbour gate open. Constants control every magic number — the countdown, the score needed, the gate's open delay — so a designer only edits one place.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# ─── Localized helper ────────────────────────────────────────────────────────
# Constants of type `message` must be declared at module scope with <localizes>.
RoundStartMsg<localizes>(S : string) : message = "{S}"
TimeUpMsg<localizes>(S : string) : message = "{S}"
GateOpenMsg<localizes>(S : string) : message = "{S}"

clifftop_cove_manager := class(creative_device):

    # ── Editable device references ────────────────────────────────────────────
    # These are constants too — the editor initialises them once.
    @editable DockTrigger   : trigger_device     = trigger_device{}
    @editable HarbourGate   : prop_mover_device  = prop_mover_device{}
    @editable HUD           : hud_message_device = hud_message_device{}

    # ── Named game constants ──────────────────────────────────────────────────
    # Explicit type annotation: Identifier : type = expression
    RoundDurationSeconds : float = 90.0   # how long the round lasts
    GateOpenDelaySecs    : float = 1.5    # pause before the gate swings open
    WarnAtSeconds        : float = 30.0   # when to flash the low-time warning

    # Integer constant — note: no auto-conversion to float, keep types consistent
    MaxActivations : int = 3   # gate can only open this many times per round

    # ── Mutable state (needs var because it changes) ──────────────────────────
    var ActivationCount : int = 0

    # ── Entry point ───────────────────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # Inferred-type constant inside a function — Identifier := expression
        StartMessage := RoundStartMsg("The cove is open! Reach the dock in time!")
        HUD.SetText(StartMessage)
        HUD.Show()

        # Subscribe to the dock trigger
        DockTrigger.TriggeredEvent.Subscribe(OnDockReached)

        # Countdown using our named constant — easy to tweak in one place
        Sleep(WarnAtSeconds)
        WarnMessage := RoundStartMsg("30 seconds left — hurry!")
        HUD.SetText(WarnMessage)

        # Wait out the remainder
        RemainingSeconds : float = RoundDurationSeconds - WarnAtSeconds
        Sleep(RemainingSeconds)

        # Round over
        EndMessage := TimeUpMsg("Time's up! The harbour gate is sealed.")
        HUD.SetText(EndMessage)
        HarbourGate.Disable()

    # ── Event handler ─────────────────────────────────────────────────────────
    OnDockReached(Agent : ?agent) : void =
        set ActivationCount = ActivationCount + 1
        if (ActivationCount <= MaxActivations):
            # Local inferred constant — captures the current count as a label
            CountLabel := GateOpenMsg("Gate opening!")
            HUD.SetText(CountLabel)
            # Spawn an async task so we can Sleep without blocking the handler
            spawn { OpenGateAfterDelay() }

    OpenGateAfterDelay()<suspends> : void =
        Sleep(GateOpenDelaySecs)   # uses the named constant
        HarbourGate.Begin()```

### Line-by-line highlights

| Line(s) | What it teaches |
|---|---|
| `RoundDurationSeconds : float = 90.0` | **Explicit-type constant** at class scope  identifier, colon, type, equals, value. |
| `MaxActivations : int = 3` | Integer constant  Verse won't silently cast this to `float`, so arithmetic stays in the right type. |
| `StartMessage := RoundStartMsg(...)` | **Inferred-type constant** inside a function  the compiler deduces `message` from the right-hand expression. |
| `RemainingSeconds : float = RoundDurationSeconds - WarnAtSeconds` | Constant derived from other constants  still immutable, computed once. |
| `Sleep(GateOpenDelaySecs)` | Using a named constant instead of a magic number makes the intent obvious. |

## Common patterns

### Pattern 1 — Module-scope constants as shared config

Declare constants **outside** any class when multiple devices on the island need the same value. They act like a config file at the top of your script.

```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Module-scope constants — visible to every class in this file
CoveSpawnHeight   : float = 512.0
MaxRoundPlayers   : int   = 8
TideIntervalSecs  : float = 20.0

tide_controller := class(creative_device):

    @editable TideTimer : timer_device = timer_device{}

    OnBegin<override>()<suspends> : void =
        # Use the shared module-scope constant directly
        TideTimer.SetTime(TideIntervalSecs)
        TideTimer.Start()
        # MaxRoundPlayers is available here too — no need to re-declare it
        LogMessage := "Tide cycle started. Max players: {MaxRoundPlayers}"
        Print(LogMessage)

Pattern 2 — Constant arrays as lookup tables

A constant can hold any Verse value, including an array. Useful for fixed wave compositions, spawn-point indices, or reward tiers.

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

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

cove_reward_manager := class(creative_device):

    @editable Granters : []item_granter_device = array{}

    # Constant array — the tier thresholds never change at runtime
    ScoreThresholds : []int = array{ 10, 25, 50, 100 }

    OnBegin<override>()<suspends> : void =
        # Read the constant array to decide which granter to activate
        FirstThreshold : int = ScoreThresholds[0]  # inferred constant, value = 10
        Print("Bronze tier unlocks at score: {FirstThreshold}")

        # Iterate the constant array
        for (Threshold : ScoreThresholds):
            Print("Threshold: {Threshold}")

        # Activate the first granter as a demo
        if (G := Granters[0]):
            G.GrantItem(false)

Pattern 3 — Constants as named boolean flags

Boolean constants document intent and make if conditions readable.

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

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

cove_gate_flags := class(creative_device):

    @editable Gate : prop_mover_device = prop_mover_device{}
    @editable HUD  : hud_message_device = hud_message_device{}

    # Named boolean constants — self-documenting, never accidentally flipped
    GateStartsOpen   : logic = false
    FriendlyFireOn   : logic = false
    ShowDebugOverlay : logic = false

    OnBegin<override>()<suspends> : void =
        if (GateStartsOpen?):
            Gate.Activate()
            HUD.SetText(StatusMsg("Harbour gate is open from the start."))
        else:
            HUD.SetText(StatusMsg("Harbour gate is sealed. Earn your way in."))

        if (ShowDebugOverlay?):
            Print("Debug overlay enabled")

Gotchas

1. Constants cannot be reassigned — use var for anything that changes

If you write MyCount : int = 0 and later try set MyCount = 1, the compiler errors. Only var fields support set. Constants are a promise to the compiler that the value is fixed.

2. No implicit int ↔ float conversion

Verse is strict about numeric types. RoundDurationSeconds : float = 90 will fail — you must write 90.0. Similarly, you cannot pass an int constant where a float is expected without an explicit conversion.

3. message parameters need <localizes> — not raw strings

A hud_message_device.SetText() takes a message, not a string. Declare a helper at module scope:

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

Then pass MyMsg("Hello cove!"). There is no StringToMessage function.

4. Inferred constants (:=) are only available inside functions

At class scope you must use the explicit form Identifier : type = expression. The short := form is only valid inside a function body or OnBegin.

5. @editable fields are constants, not variables

Fields marked @editable are set by the editor before runtime and behave exactly like constants — you cannot set them. This is a feature: it guarantees the designer's configuration is stable throughout the session.

6. Constants are evaluated once at initialization

A constant like RemainingSeconds : float = RoundDurationSeconds - WarnAtSeconds is computed when the binding is first evaluated, not lazily. If you need a value that depends on runtime state, use a var or compute it inside a function.

Guides & scripts that use timer_device

Step-by-step tutorials that put this object to work.

Build your own lesson with timer_device

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 →