Reference Verse compiles

loop + Sleep: Timed Repeating Logic on Your Island

Every great Fortnite island has something that pulses — a lighthouse beacon, a tide warning, a pirate ship's alarm bell. The `loop` expression combined with `Sleep()` is how you make Verse do something *forever*, on a schedule, without blocking everything else. Pair it with `hud_message_device` and you can keep players informed every step of the way.

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

Overview

A loop in Verse runs its body repeatedly until a break is hit or the enclosing async context is cancelled. On its own that would spin infinitely and freeze your island. Sleep(Seconds : float) is what gives time back to the engine between iterations — it suspends the current coroutine for the requested number of seconds and lets everything else keep running.

Together they are the backbone of:

  • Repeating HUD countdowns ("Tide rises in 10… 9… 8…")
  • Toggling props (disappearing docks, blinking lights)
  • Periodic game-state checks (did anyone capture the flag yet?)
  • Random-interval events (a cannon fires every 3–7 seconds)

Reach for loop + Sleep whenever you need something to happen more than once, on a schedule, for the lifetime of the game.

API Reference

hud_message_device

Used to show custom HUD messages to one or more agents.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

hud_message_device<public> := class<concrete><final>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
ShowMessageEvent ShowMessageEvent<public>:listenable(agent) Called when a Message has been Shown on-screen. Returns an Agent if it was Shown on a specified Agent's screen.
HideMessageEvent HideMessageEvent<public>:listenable(agent) Called when a Message has been Hidden on-screen. Returns an Agent if it was Hidden from a specified Agent's screen.
ClearAllMessagesEvent ClearAllMessagesEvent<public>:listenable(agent) Called when all queued Messages from all players that are affected by this HUD Message Device have been cleared.

Methods (call these to make the device act):

Method Signature Description
Show Show<public>(Agent:agent):void Shows the currently set HUD Message on Agents screen. Will replace any previously active message. Use this when the device is setup to target specific agents.
Show Show<public>():void Shows the currently set Message HUD message on screen. Will replace any previously active message.
Hide Hide<public>():void Hides the HUD message.
Hide Hide<public>(Agent:agent):void Hides the currently set HUD Message on Agents screen. Use this when the device is setup to target specific agents.
Show Show<public>(Agent:agent, Message:message, ?DisplayTime:float Displays a Custom message to a specific Agent that you define.Setting DisplayTime to 0.0 will display the HUD message persistently.If not defined, or less than 0.0 the message will show for the time set on the device.
Show Show<public>(Message:message, ?DisplayTime:float Displays a Custom message that you define for all PlayersSetting DisplayTime to 0.0 will display the HUD message persistently.If not defined, or less than 0.0 the message will show for the time set on the device.
SetDisplayTime SetDisplayTime<public>(Time:float):void Sets the time (in seconds) the HUD message will be displayed. 0.0 will display the HUD message persistently.
GetDisplayTime GetDisplayTime<public>()<transacts>:float Returns the time (in seconds) for which the HUD message will be displayed. 0.0 means the message is displayed persistently.
SetText SetText<public>(Text:message):void Sets the Message to be displayed when the HUD message is activated. Text is clamped to 150 characters.
ClearAllMessages ClearAllMessages<public>():void Clears all queued Messages from all players that are affected by this HUD Message Device.

player

Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse). Inherited members are merged from agent.

player<native><public> := class<unique><persistent><module_scoped_var_weak_map_key><epic_internal>(agent):

Walkthrough

Scenario: The Lagoon Tide Warning

Your 2D cel-shaded island has a sun-drenched lagoon cove. Every 30 seconds the tide surges — players standing on the low dock get swept away. You want a HUD countdown that ticks from 10 down to 1, shows a "TIDE SURGE!" flash, then repeats forever. A second hud_message_device persistently shows the cycle number so players know how many surges have happened.

Setup in UEFN:

  1. Place a HUD Message Device named CountdownHUD — set Display Time to 2.0 seconds.
  2. Place a second HUD Message Device named CycleHUD — set Display Time to 0.0 (persistent).
  3. Place your Verse device actor in the level and wire both HUD devices to it via the Details panel.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Helper to turn a string into a message (required — Verse has no StringToMessage)
CountdownLabel<localizes>(S : string) : message = "{S}"

tide_warning_device := class(creative_device):

    # Wire these in the UEFN Details panel
    @editable CountdownHUD : hud_message_device = hud_message_device{}
    @editable CycleHUD     : hud_message_device = hud_message_device{}

    # How many seconds between surges (the "calm" window)
    @editable CalmDuration : float = 30.0

    # How many seconds the countdown lasts before the surge
    @editable CountdownSeconds : int = 10

    OnBegin<override>()<suspends> : void =
        # Make the cycle counter persistent (DisplayTime 0.0 = forever)
        CycleHUD.SetDisplayTime(0.0)
        CycleHUD.SetText(CountdownLabel("Surge #0"))
        CycleHUD.Show()

        var SurgeCount : int = 0

        loop:
            # ── CALM PHASE ──────────────────────────────────────────
            # Wait out the calm window before the next countdown
            Sleep(CalmDuration)

            # ── COUNTDOWN PHASE ─────────────────────────────────────
            var Tick : int = CountdownSeconds
            loop:
                if (Tick <= 0):
                    break
                # Show the current tick number on the countdown HUD
                CountdownHUD.SetText(CountdownLabel("⚠ Tide in {Tick}s"))
                CountdownHUD.Show()   # replaces any previous message
                set Tick = Tick - 1
                Sleep(1.0)

            # ── SURGE PHASE ──────────────────────────────────────────
            set SurgeCount = SurgeCount + 1
            CountdownHUD.SetText(CountdownLabel("🌊 TIDE SURGE!"))
            CountdownHUD.SetDisplayTime(3.0)
            CountdownHUD.Show()

            # Update the persistent cycle counter
            CycleHUD.SetText(CountdownLabel("Surge #{SurgeCount}"))
            CycleHUD.Show()

            # Brief pause so the surge message is visible before calm resets
            Sleep(3.0)

Line-by-line breakdown:

Lines What's happening
@editable fields Devices wired in UEFN; changing CalmDuration in the editor tunes the pacing without touching code.
CycleHUD.SetDisplayTime(0.0) 0.0 means show forever — the surge counter never auto-hides.
CycleHUD.Show() Activates the persistent counter immediately at game start.
Outer loop: Runs the full calm→countdown→surge cycle indefinitely.
Sleep(CalmDuration) Suspends this coroutine for 30 s; the rest of the island runs normally.
Inner loop: + break Counts down from 10 to 1, breaking when Tick hits 0.
CountdownHUD.SetText(...) Swaps the message text each second — no new device needed.
CountdownHUD.Show() Each call replaces the previous message, so the number updates cleanly.
CountdownHUD.SetDisplayTime(3.0) Lengthens the display window just for the surge flash.
Sleep(3.0) at the end Keeps the surge message visible before the outer loop restarts.

Common patterns

Pattern 1 — Hide the HUD after a timed event (using Hide)

Sometimes you want a message to appear, linger a custom amount of time, then be explicitly cleared rather than relying on auto-dismiss. This pattern shows Hide() and ClearAllMessages().

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

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

anchor_alert_device := class(creative_device):

    @editable AlertHUD : hud_message_device = hud_message_device{}

    # Seconds the anchor-drop alert stays visible
    @editable AlertDuration : float = 5.0

    OnBegin<override>()<suspends> : void =
        # Show a persistent anchor alert at the start of every round
        AlertHUD.SetDisplayTime(0.0)   # 0.0 = persistent until we hide it
        AlertHUD.SetText(AnchorMsg("⚓ Drop anchor — hold the cove!"))
        AlertHUD.Show()

        # Let it sit for AlertDuration seconds, then hide it explicitly
        Sleep(AlertDuration)
        AlertHUD.Hide()   # explicit hide, not waiting for auto-dismiss

        # After a longer wait, clear ALL queued messages island-wide
        Sleep(20.0)
        AlertHUD.ClearAllMessages()

Key ideas: SetDisplayTime(0.0) pins the message on screen. Hide() removes it on your schedule. ClearAllMessages() is a nuclear option — it wipes every queued message for every player affected by this device.


Pattern 2 — Per-player message using Show(Agent, Message) with event subscription

When a player steps onto the dock trigger, show only that player a personalised warning using the two-argument Show overload.

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

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

dock_warning_device := class(creative_device):

    @editable DockTrigger : patchwork_trigger_device = patchwork_trigger_device{}
    @editable WarningHUD  : hud_message_device        = hud_message_device{}

    OnBegin<override>()<suspends> : void =
        DockTrigger.TriggeredEvent.Subscribe(OnPlayerStepOnDock)
        # Keep the coroutine alive forever so subscriptions stay active
        Sleep(Inf)

    OnPlayerStepOnDock(Agent : agent) : void =
        # Show a message to THIS player only — 4-second display time
        WarningHUD.Show(Agent, DockWarning("🌊 Careful — tide's coming!"), ?DisplayTime := 4.0)

Key ideas: The two-argument Show(Agent, Message, ?DisplayTime) overload targets a single player and sets the display time in one call. Sleep(Inf) keeps OnBegin alive so the subscription never drops.


Pattern 3 — Random-interval loop with GetRandomFloat

A pirate ship's cannon fires at unpredictable intervals. Use GetRandomFloat inside the loop to vary the sleep duration each cycle.

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

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

cannon_loop_device := class(creative_device):

    @editable CannonHUD  : hud_message_device = hud_message_device{}
    @editable MinDelay   : float = 3.0
    @editable MaxDelay   : float = 8.0

    OnBegin<override>()<suspends> : void =
        loop:
            # Random calm window between shots
            var WaitTime : float = GetRandomFloat(MinDelay, MaxDelay)
            Sleep(WaitTime)

            # Flash the cannon-fire alert to all players
            CannonHUD.Show(CannonMsg("💥 CANNON FIRE!"), ?DisplayTime := 2.0)

            # Check display time for debug purposes (reads the device setting)
            var CurrentDisplay : float = CannonHUD.GetDisplayTime()
            # (CurrentDisplay is 2.0 — set by the Show call above via device default)
            Sleep(2.0)   # wait for the message to finish before next cycle

Key ideas: GetRandomFloat(Low, High) returns a random float each iteration, making the loop feel organic. Show(Message, ?DisplayTime) broadcasts to all players with a custom duration. GetDisplayTime() reads back the current setting — useful for logging or dynamic adjustments.

Gotchas

1. loop without Sleep will hang your island

A bare loop: with no Sleep (or other suspending expression) is an infinite synchronous spin — it never yields to the engine and will freeze or crash your island. Always put at least one Sleep() call inside every loop body.

2. Sleep(0.0) is not a no-op

Sleep(0.0) yields until the next engine tick. It's useful when you want a loop to run once per frame without a real delay, but it's not free — it still suspends and resumes. Sleep with a negative value completes immediately without yielding, which is rarely what you want in a loop.

3. message is not a string

Verse's HUD methods (SetText, Show(Message:message, ...)) require a message type, not a raw string. You must declare a <localizes> helper:

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

Then call MyLabel("your text here"). There is no StringToMessage function.

4. Show() replaces — it does not queue

Calling Show() while a message is already visible replaces it immediately. If you want two messages to appear in sequence, call Show(), Sleep() for the display duration, then call Show() again with the next message.

5. SetDisplayTime affects future Show() calls

SetDisplayTime changes the device's stored setting. If you call Show(Message, ?DisplayTime := 4.0) the named parameter overrides the stored setting for that call only. Subsequent bare Show() calls will revert to whatever SetDisplayTime last set.

6. int and float do not auto-convert

Sleep takes a float. If your delay is stored as an int, you must cast: Sleep(Float[MyInt]) or just declare it as float from the start. Mixing types silently fails to compile.

Guides & scripts that use hud_message_device

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

Build your own lesson with hud_message_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 →