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.
nested_loops in ~90 seconds.Overview
A nested loop is simply a loop inside another loop. In Verse you have two main loop forms:
forexpression — iterates over a range or array, producing a value per element (or driving side effects).loop/break— runs indefinitely until abreakis 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:
- Place nine props in a 3 × 3 layout and name them
Platform_00throughPlatform_22. - Create a Verse device and wire all nine props to the
@editablefields below. - 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
- Generate UI Grids Dynamically with Verse LoopsPlacing UI widgets by hand doesn't scale. In this guide you'll build an entire grid of text labels programmatically using nested Verse loops, a canvas with pixel-positioned canvas_slots, and the real GetPlayerUI/AddWidge…
- Build Dynamic UI Grids with VerseStop manually placing widgets in Blueprint. Learn how to programmatically generate scalable button grids using Verse loops and calculated alignment values for precise UI layouts.
Build your own lesson with nested_loops
Generate a personalized, compile-checked lesson grounded in this exact reference.
Build a lesson →