Reference Verse compiles

Nested Loops in Verse: Grid Waves, Countdown Grids, and Multi-Pass Logic

Nested loops let you iterate over two (or more) dimensions at once — think rows and columns of platforms, waves of enemies in a grid, or a countdown that checks every player against every zone. In Verse, `for` and `loop` compose cleanly inside async coroutines, and pairing them with `Sleep()`, `creative_prop`, and device events unlocks rich, timed game behaviors that would be painful to hand-wire in the editor.

Updated Examples verified on the live UEFN compiler
Watch the Knotnested_loops in ~90 seconds.

Overview

A nested loop is simply a loop inside another loop. In Verse you have two main loop forms:

  • for expression — iterates over a range or array, producing a value per element (or driving side effects).
  • loop / break — runs indefinitely until a break is hit, ideal for game-lifetime behaviors.

Nesting these lets you:

  • Walk a 2-D grid of props (row × column) to hide/show platforms in a wave.
  • Run an outer "round" loop with an inner "per-platform" loop that resets state each round.
  • Stagger effects across a grid with Sleep() between inner iterations.

Reach for nested loops whenever your game state has two independent axes — rounds × players, rows × columns, waves × items.

API Reference

(API surface could not be resolved for this device.)

Walkthrough

Scenario: A 3 × 3 Disappearing Platform Grid

You have nine creative_prop platforms arranged in a 3 × 3 grid. Each round, the grid hides platform-by-platform in row-major order (left-to-right, top-to-bottom), waits, then reveals them all at once before starting the next round. Players must jump between platforms that are still visible — classic floor-is-lava gameplay.

Setup in UEFN:

  1. Place nine props in a 3 × 3 layout and name them Platform_00 through Platform_22.
  2. Create a Verse device and wire all nine props to the @editable fields below.
  3. Place the device in the level and hit Build Verse.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# A 3x3 grid of platforms that disappear row-by-column each round,
# then reappear together before the next round begins.
grid_disappear_device := class(creative_device):

    # Wire these nine props in the Details panel — row 0
    @editable
    Platform00 : creative_prop = creative_prop{}
    @editable
    Platform01 : creative_prop = creative_prop{}
    @editable
    Platform02 : creative_prop = creative_prop{}

    # Row 1
    @editable
    Platform10 : creative_prop = creative_prop{}
    @editable
    Platform11 : creative_prop = creative_prop{}
    @editable
    Platform12 : creative_prop = creative_prop{}

    # Row 2
    @editable
    Platform20 : creative_prop = creative_prop{}
    @editable
    Platform21 : creative_prop = creative_prop{}
    @editable
    Platform22 : creative_prop = creative_prop{}

    # Seconds between each individual platform disappearing
    @editable
    HideStagger : float = 0.4

    # Seconds all platforms stay hidden before the round resets
    @editable
    HiddenDuration : float = 2.0

    # Seconds all platforms stay visible before the next round
    @editable
    VisibleDuration : float = 3.0

    # Build the grid as a 2-D array (array of rows, each row is an array of props)
    GetGrid() : [][]creative_prop =
        array:
            array{Platform00, Platform01, Platform02}
            array{Platform10, Platform11, Platform12}
            array{Platform20, Platform21, Platform22}

    OnBegin<override>()<suspends> : void =
        # Outer loop: runs forever — one iteration = one round
        loop:
            # Make sure every platform is visible at the start of each round
            transact:
                for (Row : GetGrid(), Prop : Row):
                    Prop.Show()

            # Give players time to position themselves
            Sleep(VisibleDuration)

            # --- NESTED LOOP: hide platforms one by one ---
            # Outer for: iterate rows
            transact:
                for (Row : GetGrid()):
                    # Inner for: iterate columns within the current row
                    for (Prop : Row):
                        transact:
                            Prop.Hide()          # Hide this single platform
                            Sleep(HideStagger)   # Stagger: wait before hiding the next one

            # All platforms are now hidden — hold the dramatic pause
            Sleep(HiddenDuration)
            # loop continues → next round begins```

**Line-by-line explanation:**

| Lines | What's happening |
|---|---|
| `GetGrid()` | Returns a `[][]creative_prop` — an array of rows, each row an array of props. This is the data structure the nested loops walk. |
| `loop:` | Outer infinite loop — one full iteration is one game round. |
| `for (Row : GetGrid(), Prop : Row): Prop.Show()` | Flattened double-for in one expression — shows every prop before each round. |
| `Sleep(VisibleDuration)` | Async pause: players see all platforms for `VisibleDuration` seconds. |
| `for (Row : GetGrid()):` | Outer `for` — steps through each row array. |
| `for (Prop : Row):` | **Inner `for`** — steps through each prop in the current row. |
| `Prop.Hide()` | Calls `creative_prop.Hide()` — hides the prop and disables its collision. |
| `Sleep(HideStagger)` | Yields for `HideStagger` seconds before hiding the next prop, creating a wave effect. |
| `Sleep(HiddenDuration)` | Dramatic pause while everything is hidden. |

## Common patterns

### Pattern 1 — Flat double-for (show all at once)

When you don't need a stagger between individual props, Verse lets you chain two iteration variables in a single `for` expression. This is the most concise way to touch every cell in a grid.

```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Flashes all platforms in a 2x3 grid on/off every few seconds.
flash_grid_device := class(creative_device):

    @editable
    Row0 : []creative_prop = array{}
    @editable
    Row1 : []creative_prop = array{}

    @editable
    OnDuration : float = 1.5
    @editable
    OffDuration : float = 1.5

    GetGrid() : [][]creative_prop =
        array{Row0, Row1}

    OnBegin<override>()<suspends> : void =
        loop:
            # Single for expression with two variables — walks every cell
            for (Row : GetGrid(), Prop : Row):
                Prop.Hide()
            Sleep(OffDuration)

            for (Row : GetGrid(), Prop : Row):
                Prop.Show()
            Sleep(OnDuration)

Key point: for (Row : GetGrid(), Prop : Row) is Verse's flat nested iteration — both variables are bound in one for, and the body runs for every (Row, Prop) combination in row-major order.


Pattern 2 — Outer loop, inner for with random stagger

Use GetRandomFloat() inside the inner loop to give each platform a unique hide delay, making the grid feel organic rather than mechanical.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Random }

# Hides platforms in random order by randomizing the sleep between each hide.
random_stagger_device := class(creative_device):

    @editable
    PlatformRow0 : []creative_prop = array{}
    @editable
    PlatformRow1 : []creative_prop = array{}
    @editable
    PlatformRow2 : []creative_prop = array{}

    @editable
    MinStagger : float = 0.1
    @editable
    MaxStagger : float = 0.8
    @editable
    ResetDelay : float = 3.0

    GetGrid() : [][]creative_prop =
        array{PlatformRow0, PlatformRow1, PlatformRow2}

    OnBegin<override>()<suspends> : void =
        loop:
            # Inner nested for: hide each prop after a random delay
            for (Row : GetGrid()):
                for (Prop : Row):
                    # Random stagger makes the grid feel alive
                    Delay := GetRandomFloat(MinStagger, MaxStagger)
                    Sleep(Delay)
                    Prop.Hide()

            Sleep(ResetDelay)

            # Restore all platforms before the next round
            for (Row : GetGrid(), Prop : Row):
                Prop.Show()
            Sleep(1.0)

Pattern 3 — Outer round counter loop, inner per-row loop with increasing speed

Each round the stagger between hides shrinks, so the grid disappears faster as the game progresses. The outer loop tracks the round; the inner for drives the per-prop behavior.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Escalating difficulty: each round the platform grid disappears faster.
escalating_grid_device := class(creative_device):

    @editable
    GridRows : [][]creative_prop = array{}

    @editable
    StartingStagger : float = 0.6
    @editable
    StaggerReduction : float = 0.05   # Subtracted each round
    @editable
    MinStagger : float = 0.05          # Floor so it never goes negative
    @editable
    HiddenPause : float = 2.0
    @editable
    VisiblePause : float = 2.5

    OnBegin<override>()<suspends> : void =
        var CurrentStagger : float = StartingStagger
        loop:
            # Show all platforms
            for (Row : GridRows, Prop : Row):
                Prop.Show()
            Sleep(VisiblePause)

            # Nested loops: outer = rows, inner = props in each row
            for (Row : GridRows):
                for (Prop : Row):
                    Prop.Hide()
                    Sleep(CurrentStagger)

            Sleep(HiddenPause)

            # Speed up next round — clamp to MinStagger
            NextStagger := CurrentStagger - StaggerReduction
            if (NextStagger > MinStagger):
                set CurrentStagger = NextStagger
            else:
                set CurrentStagger = MinStagger

Gotchas

1. for in Verse is an expression, not a statement

for produces a value (an array of results). When you use it for side effects like Hide() or Show(), the result is []void — that's fine, just don't try to assign it expecting a meaningful array unless your body expression produces values.

2. Sleep() inside a nested for suspends the whole coroutine

Each Sleep(HideStagger) in the inner loop yields the coroutine for that many seconds. The outer loop iteration doesn't advance until the inner loop — including all its sleeps — finishes. This is usually what you want, but if you need rows to disappear in parallel, you need spawn for each row's inner loop.

3. loop never exits on its own — always plan your break

An infinite loop: without a break runs until the game session ends. For round-limited games, add a round counter and break when you hit the limit:

# Inside OnBegin<override>()<suspends>:void =
var Round : int = 0
loop:
    set Round = Round + 1
    if (Round > 5): break
    # ... round logic

4. int and float do NOT auto-convert

Sleep() takes a float. If you compute a stagger from integer math (e.g., Round * 2), you must write Round * 2.0 or cast explicitly. Mixing int and float arithmetic is a compile error.

5. Editable arrays ([][]creative_prop) must be pre-populated

If you expose a [][]creative_prop as @editable, UEFN currently doesn't show a nested array editor well. The workaround used in Pattern 3 is to expose each row as a separate @editable []creative_prop field and assemble them in a GetGrid() helper method — exactly as shown in the Walkthrough.

6. creative_prop fields need real prop references

A bare creative_prop{} default is a null/unset prop. If you call Hide() or Show() on an unset prop at runtime, it silently does nothing (or errors). Always wire every @editable creative_prop field to a real placed prop in the UEFN Details panel before testing.

Guides & scripts that use nested_loops

Step-by-step tutorials that put this object to work.

Build your own lesson with nested_loops

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →