Dynamic Verse UI Button Grid Generation
Tutorial intermediate compiles

Dynamic Verse UI Button Grid Generation

Updated intermediate Code 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