Reference Verse compiles

Functions in Verse: Building Reusable Game Logic

Every interesting island moment — a lighthouse beacon that pulses, a dock gate that swings open, a cove timer that counts down — is driven by reusable logic you write once and call many times. In Verse, a **function** is that reusable unit: it takes inputs, does work, and returns a result. Master functions and you master the building blocks of every creative device you will ever write.

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

Overview

A function in Verse is a named block of code that performs one focused job. You define it once, then call it from anywhere in your device — from OnBegin, from an event handler, or from another function. Functions let you:

  • Avoid copy-paste bugs — fix the logic in one place and every caller benefits.
  • Name your intentOpenDockGate() is clearer than thirty lines of inline code.
  • Compose behaviour — small functions snap together like dock planks to build complex systems.
  • Accept parameters and return values — the same FlashBeacon function can flash fast or slow depending on the float you pass in.

Reach for a function any time you find yourself writing the same logic twice, or any time a block of code deserves a name that explains why it exists.

API Reference

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

Walkthrough

Scenario: A sunny clifftop dock. When the island starts, a lighthouse beacon on the headland begins pulsing — fast at first to signal danger, then slow once the dock gate opens and the cove is safe. We model this with three custom functions: one that computes a safety message, one that pulses the beacon a given number of times at a given speed, and one that orchestrates the whole sequence.

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

# ─────────────────────────────────────────────────────────────
# dock_beacon_manager — clifftop lighthouse controller
# Place this device on your island, wire up the two light
# devices in the UEFN editor via the @editable fields.
# ─────────────────────────────────────────────────────────────
dock_beacon_manager := class(creative_device):

    # ── Editable references ──────────────────────────────────
    @editable
    BeaconLight : customizable_light_device = customizable_light_device{}

    @editable
    DockGateLight : customizable_light_device = customizable_light_device{}

    # ── Helper: pure function — no effects, just returns text ─
    # Returns a human-readable status string for the dock.
    # Pure functions (no <suspends>, no side-effects) are the
    # simplest kind — they take input and return output.
    GetDockStatus(IsSafe : logic) : string =
        if (IsSafe?):
            "Cove clear — dock gate open"
        else:
            "Danger — dock gate sealed"

    # ── Helper: suspending function — does work over time ─────
    # Pulses BeaconLight `Count` times with `Interval` seconds
    # between each flash.  <suspends> means the caller waits
    # for this function to finish before moving on.
    PulseBeacon(Count : int, Interval : float)<suspends> : void =
        var Flashes : int = 0
        loop:
            if (Flashes >= Count):
                break
            BeaconLight.TurnOn()
            Sleep(Interval / 2.0)   # on for half the interval
            BeaconLight.TurnOff()
            Sleep(Interval / 2.0)   # off for the other half
            set Flashes = Flashes + 1

    # ── Helper: opens the dock gate (turns on its indicator) ──
    OpenDockGate()<suspends> : void =
        DockGateLight.TurnOn()
        Sleep(0.5)   # brief pause so the light registers

    # ── Entry point ───────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # 1. Signal danger: 6 rapid flashes (0.3 s each)
        PulseBeacon(6, 0.3)

        # 2. Open the dock gate
        OpenDockGate()

        # 3. Signal safe: 3 slow pulses (1.2 s each)
        PulseBeacon(3, 1.2)

        # 4. Call our pure function and use its result
        StatusText := GetDockStatus(true)
        # (In a real island you'd display StatusText via a
        #  HUD message device — here it drives further logic.)```

### Line-by-line explanation

| Lines | What's happening |
|---|---|
| `GetDockStatus(IsSafe : logic) : string` | A **pure function**  takes one parameter, returns a `string`. No `<suspends>`, no side-effects. Call it anywhere, including inside expressions. |
| `PulseBeacon(Count : int, Interval : float)<suspends> : void` | A **suspending function**  it uses `Sleep`, so it must declare `<suspends>`. The caller pauses until all flashes are done. |
| `OpenDockGate()<suspends> : void` | Another suspending helper. Wrapping the gate logic in its own function means you can call it from multiple event handlers later. |
| `OnBegin<override>()<suspends> : void` | The device entry point. It calls each helper in sequence  because they are `<suspends>`, they run one after the other, not all at once. |
| `PulseBeacon(6, 0.3)` then `PulseBeacon(3, 1.2)` | **Same function, different arguments**  this is the payoff of parameterised functions. No copy-paste. |

## Common patterns

### Pattern 1 — Overloaded functions (same name, different parameter types)

Verse lets you define multiple functions with the same name as long as their parameter types differ. The compiler picks the right one at the call site.

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

# Demonstrates function overloading on a sunny dock island.
# Two versions of `DescribeWave` — one for int height, one for float.
dock_wave_reporter := class(creative_device):

    # Overload 1: wave height as a whole-number metre count
    DescribeWave(HeightMetres : int) : string =
        "Wave height: {HeightMetres} m (integer reading)"

    # Overload 2: wave height as a precise float measurement
    DescribeWave(HeightMetres : float) : string =
        "Wave height: {HeightMetres} m (sensor reading)"

    OnBegin<override>()<suspends> : void =
        IntDesc   := DescribeWave(3)      # calls overload 1
        FloatDesc := DescribeWave(3.7)    # calls overload 2
        # Use both results — e.g. feed them to a HUD device
        _ = IntDesc
        _ = FloatDesc
        Sleep(0.0)   # yield to other coroutines

Pattern 2 — Failable function with <decides>

A function marked <decides> can fail — the caller must handle that possibility inside an if expression. This is perfect for "can the player do X?" checks.

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

# Clifftop cove: only let a boat launch if the tide is low enough.
dock_launch_controller := class(creative_device):

    # Returns the launch clearance string, or FAILS if tide is too high.
    # <decides> means callers must handle the failure case.
    GetLaunchClearance(TideLevel : float)<decides> : string =
        # The failure condition — if tide >= 2.5 m, this expression
        # fails and the enclosing `if` takes the else branch.
        TideLevel < 2.5
        "Launch cleared — tide at {TideLevel} m"

    OnBegin<override>()<suspends> : void =
        CurrentTide : float = 1.8

        if (Msg := GetLaunchClearance(CurrentTide)):
            # Success path — tide is low, we have a message
            _ = Msg   # hand Msg to a HUD device in a real island
        else:
            # Failure path — tide too high, abort launch
            Sleep(0.0)

        Sleep(0.0)

Pattern 3 — Function as a value (passing functions around)

Verse treats functions as first-class values. You can store a function in a variable and call it later — useful for configurable callbacks.

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

# Shore patrol: choose a patrol behaviour at runtime by
# storing a function reference in a variable.
dock_patrol_selector := class(creative_device):

    # Two patrol strategies — same signature, different logic
    SlowPatrol(Steps : int)<suspends> : void =
        var I : int = 0
        loop:
            if (I >= Steps): break
            Sleep(1.5)   # slow cadence
            set I = I + 1

    FastPatrol(Steps : int)<suspends> : void =
        var I : int = 0
        loop:
            if (I >= Steps): break
            Sleep(0.4)   # fast cadence
            set I = I + 1

    # Runs whichever patrol function is passed in
    RunPatrol(PatrolFn : type{_(:int)<suspends>:void}, Steps : int)<suspends> : void =
        PatrolFn(Steps)

    OnBegin<override>()<suspends> : void =
        # Danger mode — fast patrol first
        RunPatrol(FastPatrol, 4)
        # Safe mode — slow patrol after
        RunPatrol(SlowPatrol, 2)

Gotchas

1. <suspends> is contagious

If your function calls Sleep (or any other <suspends> function), it must declare <suspends> itself. And the function that calls it must also declare <suspends>, all the way up to OnBegin. Forgetting one link in the chain is a compile error.

2. int and float are never auto-converted

Verse will not silently turn 3 into 3.0. If PulseBeacon expects a float interval, pass 0.3 (with the decimal point), not 0. Mixing them is a type error.

3. <decides> functions must be called inside if (or another failable context)

Calling a <decides> function outside an if is a compile error. Always wrap it:

# WRONG — compile error
Result := GetLaunchClearance(1.8)

# RIGHT
if (Result := GetLaunchClearance(1.8)):
    _ = Result

4. Pure functions cannot call suspending ones

A function without <suspends> cannot call a function that has <suspends>. Keep your pure helper functions free of Sleep and device calls that suspend.

5. message vs string

Device properties that display text (like HUD message devices) expect a message (a localised type), not a raw string. If you build a string inside a function and want to display it, declare a localisation wrapper:

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

Then pass MyMsg(GetDockStatus(true)) to the device. There is no StringToMessage built-in.

6. Unused variables are compile errors

If you call a function that returns a value and don't use it, assign it to _:

_ = GetDockStatus(false)

Guides & scripts that use define_a_function

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

Build your own lesson with define_a_function

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 →