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 intent —
OpenDockGate()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
FlashBeaconfunction 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)