Verse Async Timers: Live UI Countdowns with Sleep()
What you'll learn
- How
Sleep(Seconds)yields to the engine inside a<suspends>context instead of blocking. - How to attach a real text widget to a player's canvas with
GetPlayerUI[]andAddWidget. - How to update that widget's text every second inside a
loop. - Using
GetRandomFloat()to vary the timer duration, and removing the widget when finished.
How it works
Sleep(Seconds) suspends the current coroutine for the given number of seconds and then resumes right where it left off. Because it yields rather than busy-waits, the rest of the game keeps running — physics, rendering, and other coroutines all continue. That's why any function that calls Sleep must be marked <suspends> (like OnBegin<override>()<suspends>).
To show the countdown, we use the Player UI system. GetPlayerUI[Player] is a fallible call (note the [] brackets) — it can fail if the player has no UI, so we bind it inside an if. Once we have the player_ui, we build a text_block widget, wrap it in a canvas with a canvas_slot, and call AddWidget(...). Each second we call SetText(...) on the widget to refresh the display, then Sleep(1.0). When time runs out we call RemoveWidget(...) to clean up.
Key correctness notes:
GetRandomFloat(Low, High)returns afloat— call it with parentheses and just store the value.- Reassign a mutable with
set:set CurrentTime = CurrentTime - 1.0(there is no-=). - The break condition uses
<=, andbreakonly works inside aloop.
Let's build it
This device picks a random countdown between your editable min/max, shows it on every player's screen, and updates the number each second until it reaches zero.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Random }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/UI }
countdown_ui_device := class<concrete>(creative_device):
# Editable range for the random countdown duration (in seconds).
@editable MinSeconds : float = 5.0
@editable MaxSeconds : float = 15.0
OnBegin<override>()<suspends>: void =
# 1. Pick a random duration. GetRandomFloat returns a float -> use ().
Duration : float = GetRandomFloat(MinSeconds, MaxSeconds)
Print("Starting countdown for {Duration} seconds")
# 2. Run the countdown for every player currently in the playspace.
for (Player : GetPlayspace().GetPlayers()):
spawn { RunCountdownFor(Player, Duration) }
# Displays and ticks a countdown on one player's screen, then cleans up.
RunCountdownFor(Player : player, Seconds : float)<suspends>: void =
# GetPlayerUI is fallible -> bind it with [] inside an if.
if (UI := GetPlayerUI[Player]):
# 3. Build a text widget and place it on a canvas slot.
TimerText : text_block = text_block{DefaultText := ToMessage("...")}
Screen : canvas = canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.1}, Maximum := vector2{X := 0.5, Y := 0.1}}
Alignment := vector2{X := 0.5, Y := 0.5}
SizeToContent := true
Widget := TimerText
UI.AddWidget(Screen)
# 4. Count down one second at a time.
var CurrentTime : float = Seconds
loop:
if (CurrentTime <= 0.0):
break
# SetText updates the on-screen widget each tick.
# Round is fallible -> bind it inside an if before using.
if (RemainingSeconds := Round[CurrentTime]):
TimerText.SetText(ToMessage("Time left: {RemainingSeconds}s"))
Sleep(1.0) # yields to the engine for one second
set CurrentTime = CurrentTime - 1.0
# 5. Time is up: show a final message, then remove the widget.
TimerText.SetText(ToMessage("Time's up!"))
Sleep(1.5)
UI.RemoveWidget(Screen)
# Helper: turn a string into the message type text widgets expect.
ToMessage<localizes>(Value : string) : message = "{Value}"```
## Try it yourself
1. Create the Verse device, add this code, then build Verse code.
2. Drop the `countdown_ui_device` into your level and press Launch Session.
3. Watch the countdown appear near the top-center of your screen and tick down every second.
4. Tweak `MinSeconds` / `MaxSeconds` in the Details panel to change the range.
5. Challenge: change `Sleep(1.0)` to `Sleep(0.5)` and decrement by `0.5` for a half-second tick — notice the game stays fully responsive.
## Recap
- `Sleep(Seconds)` suspends *only your task* and hands control back to the engine, so the game never freezes.
- Any function that uses `Sleep` (or awaits events) must be `<suspends>`.
- `GetPlayerUI[Player]` is fallible — bind it with `[]` in an `if`; then use `AddWidget` / `RemoveWidget` and `SetText` on a `text_block` to drive real on-screen UI.
- Use `GetRandomFloat()` for variety, `set` to update mutables, and `break` to exit the `loop` when the timer hits zero.
Check your understanding
Test yourself with an interactive quiz and track your progress + earn XP — free for members.
Turn this into a guided course
Add Implementing Asynchronous Timers with Sleep() in Verse to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.
Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.