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:
- Place a
HUD Message DevicenamedCountdownHUD— set Display Time to2.0seconds. - Place a second
HUD Message DevicenamedCycleHUD— set Display Time to0.0(persistent). - 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.