Live Widget State Binding with Verse
Tutorial intermediate compiles

Live Widget State Binding with Verse

Updated intermediate Code verified

What you'll learn

  • Building a live widget (a slider_device's Widget) and attaching it to each player's player_ui.
  • Correctly binding the fallible GetPlayerUI[] call inside a failure context.
  • Pushing real-time updates with a coroutine loop and Sleep.
  • Doing safe integer-to-float normalization WITHOUT the % operator (error 3100) or fallible /.

How it works

Verse UI binding bridges your simulation state to what each player sees on screen. The key API is GetPlayerUI[Player] — it is <decides> (fallible) because a player may not have an associated player_ui yet, so we must call it with [] inside an if condition and bind the result: if (UI := GetPlayerUI[Player]):.

Once we hold a player_ui, we call UI.AddWidget(...) and hand it our device's Widget. We use a slider_device's .Widget slot as our concrete on-screen element. From there a coroutine loop reads a mutable int metric, normalizes it into the slider's 0.0–1.0 range, and calls SetValue(...) every quarter second.

Why the original failed (error 3100): Verse has no % operator and integer / is fallible. We avoid both by converting to float first (CurrentCurrency * 1.0) and dividing by a float we already validated is greater than zero. All mutation of the tracked value uses set.

Let's build it

Place this device on your island and drag a slider_device into the TargetSlider slot. The slider's built-in widget is what appears on the player's canvas.

using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }

# Live-binds a slider widget to each player's UI and updates it in real time.
my_stat_sync_device := class<concrete>(creative_device):

    # Wire a slider_regular widget here; its widget is shown on screen.
    @editable TargetSlider : slider_regular = slider_regular{}

    # The gameplay metric we visualize. Mutable so we can `set` it later.
    @editable var CurrentCurrency : int = 0

    # The slider's max value (its 100% mark). Must be > 0 to normalize.
    @editable MaxCurrency : float = 100.0

    OnBegin<override>()<suspends>: void =
        Print("Launching stat sync...")

        # Configure the slider's range once so SetValue maps cleanly.
        TargetSlider.SetMinValue(0.0)
        TargetSlider.SetMaxValue(1.0)

        # Reach every player and attach the widget to their UI.
        for (Player : GetPlayspace().GetPlayers()):
            # GetPlayerUI is <decides>: bind it with [] in a failure context.
            if (UI := GetPlayerUI[Player]):
                # Put the slider widget onto this player's canvas.
                UI.AddWidget(TargetSlider)
                Print("Widget added to player canvas.")

        # Coroutine loop: push the latest metric to the slider forever.
        loop:
            # VC_IntMod(No, and) no fallible int / : convert to float, then divide.
            var NormVal : float = 0.0
            if (MaxCurrency > 0.0):
                set NormVal = (CurrentCurrency * 1.0) / MaxCurrency
            # SetValue clamps into the slider's [0,1] range automatically.
            TargetSlider.SetValue(NormVal)
            # Yield to other coroutines between updates.
            Sleep(0.25)

# Verse Cortex (UEFN-227): total integer modulo — Verse has no `%` operator.
# Wraps the native fallible Mod[] so it can be used in any context.
VC_IntMod(A:int, B:int):int =
    if (R := Mod[A, B]):
        R
    else:
        0
# Verse Cortex (UEFN-227): total integer division — Verse `/` on ints is
# fallible and yields a rational; this returns a plain int (floor), 0 when B<=0.
VC_IntDiv(A:int, B:int):int =
    if (B > 0, R := Mod[A, B], Q := Floor[(A - R) * 1.0 / (B * 1.0)]):
        Q
    else:
        0```

## Try it yourself

- **Drive the value from events**: Give the device an `@editable GainTrigger : trigger_device` and subscribe `GainTrigger.TriggeredEvent.Subscribe(OnGain)`; inside `OnGain` do `set CurrentCurrency = CurrentCurrency + 10`. The loop reflects it instantly.
- **Add a second slider**: Add another `slider_device` for `Rebirths`, attach it in the same `for` loop, and update it in the loop body.
- **Clamp inputs**: Before normalizing, guard against negatives with `if (CurrentCurrency < 0) { set CurrentCurrency = 0 }` so the slider never goes below empty.

## Recap

Widget state binding keeps your presentation synced with gameplay. You attached a `slider_device`'s `Widget` to each player via the fallible `GetPlayerUI[Player]` binding and `AddWidget`, then pushed live updates with a `loop` + `Sleep` coroutine. The two rules that keep this compiling: fallible `[]` calls belong inside an `if`/`for` condition, and you avoid `%` / fallible integer `/` by normalizing through floats.

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 Widget State Binding & Real-Time UI Updates 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