Dynamic Verse UI Button Grid Generation
Tutorialintermediatecompiles

Dynamic Verse UI Button Grid Generation

Updated intermediateCode verified

What you'll learn

You'll learn the known-good UEFN pattern for generating a grid of UI buttons from a data list. We'll cover the three correctness traps that break most attempts: GetPlayerUI[] is a failable (<decides>) call and must live in a failure context; widgets are laid out by adding slots to stack_box rows, not by hand-positioning; and a click is awaited with Button.OnClick().Await().

How it works

  1. Define the data: a flat []string of labels drives how many buttons exist.
  2. Build the buttons: for each label we create a button_loud, store it in a []button_loud.
  3. Lay out the grid: we add each button to a horizontal row stack_box; when a row fills up (GridWidth columns) we start a new row, then wrap all rows in a vertical stack_box placed in a canvas.
  4. Show it: if (PlayerUI := GetPlayerUI[Player]) succeeds, we call PlayerUI.AddWidget(Canvas, ...) so the grid appears on screen and consumes input.
  5. React: a coroutine loops on Button.OnClick().Await() and prints which label was clicked, using .Player from the returned message.

Let's build it

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

# Dynamically builds a grid of clickable buttons and shows it to every player.
ui_grid_device := class<concrete>(creative_device):

    @editable
    GridWidth : int = 3              # columns per row; grid reshapes automatically

    # The data list that drives the grid. Add labels -> more buttons appear.
    ButtonLabels : []string = array{"Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota"}

    OnBegin<override>()<suspends>: void =
        # Vertical container that will hold each row of buttons.
        Column := stack_box{ Orientation := orientation.Vertical }
        # Track buttons + the row currently being filled.
        var Buttons : []button_loud = array{}
        var Row : stack_box = stack_box{ Orientation := orientation.Horizontal }
        var ColCount : int = 0

        for (Label : ButtonLabels):
            Button := button_loud{ DefaultText := StringToMessage(Label) }
            set Buttons += array{ Button }
            # Add this button as a slot in the current horizontal row.
            Row.AddWidget(stack_box_slot{ Widget := Button })
            set ColCount += 1
            # Row is full -> commit it to the column and start a fresh row.
            if (ColCount >= GridWidth):
                Column.AddWidget(stack_box_slot{ Widget := Row })
                set Row = stack_box{ Orientation := orientation.Horizontal }
                set ColCount = 0
        # Commit any trailing partial row.
        if (ColCount > 0):
            Column.AddWidget(stack_box_slot{ Widget := Row })

        # Place the whole column on a canvas, centered on screen.
        Canvas := canvas{}
        Canvas.AddWidget(canvas_slot{
            Widget := Column,
            Anchors := anchors{ Minimum := vector2{X:=0.5,Y:=0.5}, Maximum := vector2{X:=0.5,Y:=0.5} },
            Alignment := vector2{X:=0.5,Y:=0.5}
        })

        # Add the canvas to every player's UI (GetPlayerUI is failable!).
        for (Player : GetPlayspace().GetPlayers()):
            if (PlayerUI := GetPlayerUI[Player]):
                PlayerUI.AddWidget(Canvas, player_ui_slot{ InputMode := ui_input_mode.All })

        # Listen for clicks on each generated button.
        for (Index -> Button : Buttons):
            spawn { HandleClicks(Button, Index) }

    # Helper: convert a plain string into a localizable message for widgets.
    StringToMessage<localizes>(Value : string) : message = "{Value}"

    # Awaits clicks forever and reports which label fired.
    HandleClicks(Button:button_loud, Index:int)<suspends>: void =
        loop:
            Msg := Button.OnClick().Await()   # widget_message carries .Player
            if (Label := ButtonLabels[Index]):
                Print("Clicked button {Index}: {Label}")```

## Try it yourself

1. Drag the `ui_grid_device` into your level and press play — a 3x3 grid appears.
2. Set `GridWidth` to 4 and add a few more strings to `ButtonLabels`; the rows reflow automatically.
3. Click buttons and watch the Output Log print the index and label.
4. Try `GridWidth := 1` to get a vertical menu, or a large width for a single wide row.

## Recap

You now know how to: generate `button_loud` widgets from a data list; arrange them into rows/columns by adding `stack_box_slot`s to nested `stack_box`es; place that inside a `canvas` and push it to the screen via the failable `GetPlayerUI[]` + `AddWidget`; and handle clicks with `Button.OnClick().Await()`. Because the layout is driven by the list, editing the data reshapes the entire grid — no editor widget tree required.

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 Dynamic UI Grid Generation via Verse List Comprehension 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