Verse Async Timers: Live UI Countdowns with Sleep()
Tutorial beginner compiles

Verse Async Timers: Live UI Countdowns with Sleep()

Updated beginner Code verified

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[] and AddWidget.
  • 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 a float — 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 <=, and break only works inside a loop.

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.

Comments

    Sign in to vote, comment, or suggest an edit. Sign in