Generate UI Grids Dynamically with Verse Loops
Tutorial intermediate compiles

Generate UI Grids Dynamically with Verse Loops

Updated intermediate Code verified
Narrated lesson video — auto-generated from this guide. More shorts

What you'll learn

You will learn how to:

  • Use nested for loops to iterate over rows and columns
  • Convert loop indices into pixel offsets with float arithmetic
  • Build canvas_slots with Anchors and Offsets for absolute pixel layout
  • Add a canvas widget to each player's screen via GetPlayerUI and AddWidget
  • Use GetPlayerUI correctly inside a <decides> (failure) context

How it works

Canvas and canvas_slot

The canvas is a free-form container. You push children onto it at runtime with Canvas.AddWidget(Slot:canvas_slot). Each canvas_slot carries:

  • Anchors — an anchors struct with Minimum/Maximum vector2 values in 0.0–1.0 screen fractions. Pinning both to {X:0.0, Y:0.0} anchors the slot to the screen's top-left corner.
  • Offsets — a margin (Left, Top, Right, Bottom) that shifts the widget in pixels from its anchor. With a top-left anchor, Left/Top become the absolute pixel position.
  • Widget — the actual child widget to display.

Grid math

For a grid with Cols columns spaced CellSize pixels apart, the X offset of column Col is just OriginX + (Col * CellSize). Rows work the same way with Row. Because Col is an int and CellSize is a float, we cast with Col * 1.0 before multiplying so the types line up.

GetPlayerUI is a <decides> call

GetPlayerUI(Player) has the <decides> effect — it can fail if a player has no UI. That means you MUST call it in a failure context: if (PlayerUI := GetPlayerUI[Player]):. The bound PlayerUI is a plain player_ui handle (NOT an option). Calling it as a bare statement triggers compiler error 3512.

Let's build it

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

# Builds a Rows x Cols grid of text labels on every player's screen UI.
dynamic_ui_grid_device := class<concrete>(creative_device):

    @editable Rows : int = 3        # number of grid rows
    @editable Cols : int = 4        # number of grid columns
    @editable CellSize : float = 80.0  # pixel spacing between cell origins
    @editable OriginX : float = 100.0  # top-left X offset, in pixels
    @editable OriginY : float = 100.0  # top-left Y offset, in pixels

    OnBegin<override>()<suspends> : void =
        # One canvas holds every cell for the whole grid.
        GridCanvas : canvas = canvas{}

        # Nested loops: Row 0..Rows-1, Col 0..Cols-1.
        for (Row := 0..Rows - 1):
            for (Col := 0..Cols - 1):
                # Index -> pixel position (cast ints to float for the math).
                PixelX : float = OriginX + (Col * 1.0 * CellSize)
                PixelY : float = OriginY + (Row * 1.0 * CellSize)

                # Each cell is a simple text label.
                CellWidget : text_block = text_block{
                    DefaultText := StringToMessage("[{Row},{Col}]")
                }

                # Pin to top-left (anchor 0,0); place via pixel Offsets.
                Slot : canvas_slot = canvas_slot{
                    Anchors := anchors{
                        Minimum := vector2{X := 0.0, Y := 0.0},
                        Maximum := vector2{X := 0.0, Y := 0.0}
                    },
                    Offsets := margin{
                        Left := PixelX, Top := PixelY,
                        Right := 0.0, Bottom := 0.0
                    },
                    Alignment := vector2{X := 0.0, Y := 0.0},
                    SizeToContent := true,
                    Widget := CellWidget
                }

                # Add this cell to the canvas at runtime.
                GridCanvas.AddWidget(Slot)

        # Push the finished canvas onto each player's HUD.
        for (Player : GetPlayspace().GetPlayers()):
            if (PlayerUI := GetPlayerUI[Player]):
                PlayerUI.AddWidget(GridCanvas)

StringToMessage<localizes>(Value : string) : message = "{Value}"```

## Try it yourself

1. In UEFN, create a new Verse file, paste the class, and **Build Verse Code**.
2. Drag the `dynamic_ui_grid_device` from the Content Browser into your level.
3. In the Details panel, set `Rows`, `Cols`, and `CellSize` to taste.
4. Launch a session  every player should see the grid of `[Row,Col]` labels in the top-left.
5. Change `Cols` to `6` and rebuild: the grid widens with a single edit, proving the loop-driven layout.

## Recap

- Nested `for` loops over `0..Rows-1` and `0..Cols-1` generate one cell per iteration.
- Loop indices become pixel offsets via `Origin + (Index * 1.0 * CellSize)`.
- A top-left anchor (`Minimum`/`Maximum` both `0,0`) plus `Offsets` gives absolute pixel placement.
- `GridCanvas.AddWidget(Slot)` builds the grid at runtime; `PlayerUI.AddWidget(GridCanvas)` shows it.
- `GetPlayerUI[Player]` is a `<decides>` call and must live inside an `if` failure context.

Check your understanding

Test yourself with an interactive quiz and track your progress + earn XP — free for members.

Want this as a guided course?

Build your own free, step-by-step lesson plan on Dynamic UI Grid Generation via Verse List Iteration — or pick any topic. Create a free account and the course builder turns it into compile-checked lessons, worked examples, and quizzes, tailored to your level.

Source
Verse Island

© Biloxi Studios Inc. — original Verse Island content.

Sources & licensing

Original tutorial generated by Verse Island from the indexed 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