Verse Wiki — the Verse handbook for Blueprint authors
Chapter 2 · Lesson 5

Structure Mapping: From Node Graph to Indented Block

The last lesson settled "what things are called". This one settles "how things are arranged". A Blueprint graph is a picture spread across a two-dimensional plane: you follow the wires with your eyes. Verse is text you read from top to bottom: order and indentation have already drawn the wires for you. This lesson translates every kind of wire and every kind of box, one at a time, into text structure.

1. A Graph Contains Only Four Kinds of Thing

Open any Blueprint graph, however messy, and everything you can see falls into four categories: white execution wires, colored data wires, the nodes themselves, and comment boxes. Translate those four, and you own the whole structural conversion.

White execution wires → order plus indentation. This is the least intuitive one: in Verse, the execution wire has no counterpart at all. No keyword, no symbol, no arrow. The reason is simple — order itself is the execution wire. When one line finishes, the next line runs; that is the white wire. Blueprints need drawn wires because nodes can sit anywhere on the plane, and without a wire you would not know which runs first; text only ever has one direction, so the wire becomes unnecessary. All the effort you spend tidying wires on the graph is simply deleted in Verse.

Colored data wires → expression nesting, or a variable name. Dragging a wire from node A's output pin to node B's input pin turns into one of two written shapes: either drop A straight inside B's parentheses — B(A()), where one layer of parentheses is one wire; or give the intermediate result a name first — X := A(), then write B(X). The first is shorter, the second reads better. Both are correct; which one you pick depends on how long that wire is and whether you reuse it.

Nodes → function calls. That box with pins on the graph is Name(arguments) in text. Print String becomes Print("Hi"); Get Player Count becomes GetPlayerCount(). Pin names become argument positions or argument names, and that's the whole story.

Comment boxes → # comments. A Blueprint comment box (select some nodes, press C) fences off a region, tints it, and gives it a title. Verse's # only comments out a single line — it cannot "fence off a region". But it doesn't really need to: in Verse, "these lines belong to the same block" is already stated by the indentation itself. Indentation took over the fencing job, leaving # with only the title job.

Put the four together, and a three-node graph looks like this:

score_board_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

score_board_device := class(creative_device):

    # Comment box → # comment: a title for the little section below
    Score:int = 40

    OnBegin<override>()<suspends>:void =
        # Data wire → expression nesting: one layer of parens is one wire
        Print("Total: {Score + 10}")

        # Or name the intermediate result first, then use it
        Total := Score + 10
        Print("Total: {Total}")

Blueprint translation: Score is an integer in the variables panel; OnBegin is that red Event BeginPlay node; the four indented lines below it are four nodes strung along the white wire in order. Score + 10 is an Add node whose output runs down a data wire into Print String's input — written as text, that wire is just Score + 10 sitting inside the curly braces. The two lines below are the same wire drawn a different way: pull it out into Total first, then plug it in.

2. One Node, Several Exits

"Execution wires are just order" runs into a problem immediately: some nodes have more than one output execution pin. Branch has True and False, Sequence has Then 0 / Then 1 / Then 2, ForEach has Loop Body and Completed. How does a single straight column of text say "the path splits here"?

The answer is the indented block: one exit = one indented block. However many wires the node fans out to the right on the graph, that's how many blocks it fans out inward in the text.

turret_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

turret_device := class(creative_device):

    var Ammo:int = 1

    OnBegin<override>()<suspends>:void =
        # Branch's True pin → the block indented after the colon
        if (Ammo > 0):
            set Ammo -= 1
            Print("Fire")
        # Branch's False pin → the block indented after else
        else:
            Print("Empty")

        # Sequence's Then 0 / Then 1 / Then 2 → three lines at the same indent
        Print("One")
        Print("Two")
        Print("Three")
Blueprint node Output execution pins How it's written in Verse
Branch True / False the block under if (…): / the block under else:
Sequence Then 0 / Then 1 / Then 2 lines 1 / 2 / 3 at the same indent (Sequence has no counterpart)
ForEach / For Loop Loop Body / Completed the block under for (…): / the next line after the indent steps back
Switch on Int / String one pin per case a chain of ifs, or case in a later chapter

The row worth staring at is Sequence: in Verse it simply does not exist. The only reason you need a Sequence node in Blueprints is that one execution pin can hold one wire, so doing three things in a row requires a dedicated node to fan the wire out. In text, "do three things in a row" is three lines — no node required. Reroute nodes (the little waypoints you add to tidy wires) vanish for the same reason: their equivalent is naming an intermediate result, and you do that for readability, not to stop wires from tangling.

One more thing: on the graph, True and False are two parallel wires drawn one above the other, and in Verse the if block and the else block are also written one above the other. The sense of direction is identical — it has just been flattened from two dimensions into one. The instinct you built up following wires with your eyes still works here.

3. The Rules of a Code Block: A Colon Opens It, Indentation Assigns It

Stated in full, there are only three rules:

vault_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

vault_device := class(creative_device):

    OnBegin<override>()<suspends>:void =
        Print("Start")
        for (Round := 1..2):
            Print("Round {Round} begins")
            if (Round > 1):
                Print("This is the last round")
            Print("Round {Round} ends")
        Print("All done")

Count the indents and you get four levels: indent 0 is the module's outermost layer (the using lines and the class name); indent 4 is the class body; indent 8 is the function body; indent 12 is the for body; indent 16 is the if body. In Blueprints this is three layers of nested wiring: class graph → Event BeginPlay's execution wire → ForLoop's Loop Body pin → Branch's True pin. The only difference is that Blueprints spread those three layers across one flat plane joined by wires, so you have to follow a wire to know which layer you're on; Verse stacks them into a staircase, and the number of indents is the layer number — countable at a glance.

Now look at the two places where the indentation steps back. Print("Round {Round} ends") steps back to indent 12, meaning it has left the if — whichever way that Branch went, True or False, this line runs. It is wired to the point where the Branch's two paths merge again. The last line, Print("All done"), steps back to indent 8, meaning it has left the for — it is wired to ForLoop's Completed pin. In Blueprints, "merge the two branches" and "the loop finished" both mean dragging a wire back and reconnecting it; in Verse, you just pressed the space bar four fewer times.

4. Line by Line: Where Did It Jump To on the Graph?

Reading indentation isn't enough — you have to watch it run. The gate device below has 2 keys and 3 rounds of door-opening, a branch nested inside a loop, which uses everything from the last two sections. Click "Run Next Step"; every note tells you which node would be highlighted and which wire would be lit if this were a Blueprint graph.

gate_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

gate_device := class(creative_device):

    var Keys:int = 2

    OnBegin<override>()<suspends>:void =
        for (Round := 1..3):
            if (Keys > 0):
                set Keys -= 1
                Print("Round {Round}: opened, {Keys} left")
            else:
                Print("Round {Round}: out of keys")
Output Log

Click "Run Next Step" to watch the code execute line by line.

Look back at the whole thing: 14 lines of code, only 6 of which actually execute, and it fully expresses "a loop wrapped around a two-exit branch". Drawn as a Blueprint, the same thing needs Event BeginPlay, ForLoop, Branch, Set and two Print Strings — six nodes and at least seven wires — and you'd probably spend a while shuffling their positions so the wires don't cross. That isn't a knock on Blueprints; graphs have their own strengths (the next lesson covers the ones Verse can't give you). But on structure specifically, text wins clean.

5. Evaluation Order: Blueprints Pull on Demand, Verse Goes Inside-Out

The structure is translated; one invisible difference remains: when the values get computed. This one isn't written on the graph or in the code, but it will trip a Blueprint author hard.

Blueprints pull on demand. Nodes on data wires aren't attached to the white wire, so they never run on their own; only when some downstream node needs that value does the engine pull back along the data wire and compute it, once. So a single Pure node with three wires leaving its output pin gets computed three times — on the graph you only see one box, and your gut says "computed once".

Verse goes left to right, inside out (push). When execution reaches a line, the expressions on that line are evaluated right there: the innermost parentheses first, handing their value outward one layer at a time; several arguments at the same layer go left to right. No deferral, no caching. Write it twice, it runs twice.

shop_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

shop_device := class(creative_device):

    var Coins:int = 100

    # A little "get the price" function that logs a line to expose each call
    # (how functions are written is Lesson 19 — here we just count the calls)
    Price():int =
        Print("-- computed the price once")
        30

    OnBegin<override>()<suspends>:void =
        # Price() written twice means two calls, so two log lines
        if (Coins > Price()):
            Print("Affordable, {Coins - Price()} left")

        # Want it computed once? Give it a name first
        P := Price()
        if (Coins > P):
            Print("Affordable, {Coins - P} left")

The first half logs "computed the price once" twice; the second half logs it once. The evaluation order of Coins - Price() is worth reciting too: innermost Price() first, giving 30; then the subtraction, giving 70; and only then is 70 handed to string interpolation. Inside out — the same direction as starting from the leftmost node on the graph and passing values rightward along the data wires.

Where the trap is. Blueprints train the instinct that "Pure nodes are free, drop one in and drag as many wires as you like" — because a Pure node looks like a value, not like a call. Carry that instinct into Verse and you'll write GetSomething() over and over inside a function, when that function might be walking an entire array each time. The flip side: this trap existed in Blueprints all along (three wires is three evaluations), the picture just hid it; Verse spells it out in plain characters — you wrote it twice, and there it is. A trap you can see isn't much of a trap. The rule is simple: if a value gets used more than once, name it first.

6. Your Turn: Put the Structure Back

Three block-opening keywords have been dug out below. The comments tell you which pin each one corresponds to on the graph — fill them in accordingly:

arena_gate_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

arena_gate_device := class(creative_device):

    var Lives:int = 2

    OnBegin<override>()<suspends>:void =
        # Three rounds: the block below is ForLoop's Loop Body pin
        ____ (Round := 1..3):
            # A Branch: what's indented after the colon is the True wire
            ____ (Lives > 0):
                set Lives -= 1
                Print("Round {Round}: entered")
            # The False wire: same indent, sitting beside the block above
            ____:
                Print("Round {Round}: out of lives")

Blueprint Cross-Reference

There's no new syntax to memorize in this lesson — just one table to know cold. On the left is what you can see on the graph; on the right is its shape in text. The differences column is the point.

Element on the Blueprint graph Text structure in Verse Difference
White execution wire (Exec) Writing order + indentation (no symbol at all) Can't be miswired, can't be broken, and there are no "orphan nodes you forgot to connect" — the cost is that you can't route it into any shape you like
Colored data wire (between data pins) Expression nesting B(A()), or a name X := A() A Blueprint data wire can stretch across half the graph; a Verse name is only valid inside its own indented block
Node (function call node) Function call Print("Hi") Whether a Blueprint node is pure is written into how it looks; in Verse it's written into the function signature's specifiers, and the compiler checks it (see extra x2)
Comment box (select nodes, press C) # line comment # can't fence off a region — but "these lines are one block" is already said by the indentation
Branch's True / False pins the if (…): block and the else: block A Verse if condition takes more than a boolean — it also takes "expressions that might not pass" (Lesson 12)
Sequence node Consecutive lines at the same indent No counterpart: text does several things in a row natively, so no fan-out node is needed
Reroute node (wire-tidying waypoint) Intermediate variable X := … Blueprints add waypoints so wires don't tangle; Verse adds names so humans read better and the value is computed once
ForEach's Loop Body / Completed the for (…): block / the next line after the indent steps back "What happens after the loop finishes" means dragging a wire back in Blueprints; in Verse it's one less level of indentation
Pull-on-demand evaluation of data wires Evaluated left to right, inside out, when that line runs Both sides compute once per use, but Blueprints hide the repeated evaluation in the picture while Verse spells it out in characters

All of these differences share one root cause: Blueprints are two-dimensional, Verse is one-dimensional. Two dimensions give you freedom of placement, at the cost of having to state "which runs first" and "which belongs to which" with extra drawn wires. One dimension only ever has one direction and one depth, so order and ownership answer themselves — at the cost of not being able to park two chunks of logic side by side and compare them.

So what really has to change when you migrate isn't your hands, it's your eyes: you used to read a graph as "find the red event node, follow the white wire"; from now on you read code as "find the function name, read downward, and every indent means you went one layer in". That switch is far quicker than memorizing syntax — most people are through it in half a day.

Level Challenge

Three mini-challenges to check the table stuck. Wrong answers can be retried forever, zero penalty.

What does the white execution wire from Blueprints correspond to in Verse?

How do you write a Sequence node's Then 0 / Then 1 / Then 2 in Verse?

A piece of code writes Price() in the condition, and writes Price() again in the interpolation on the next line. How many times is Price called?

Further Reading

Advanced · EXTRA

Execution Wires vs Expressions: Why Verse Has No "White Wire"

Blueprints use white wires and colored wires to split "do a thing" from "compute a value". Verse tore that line down: everything is an expression, even if and code blocks have values. What does that mean for you?

Open the extra →

Deep Dive · EXTRA

Pure Nodes and Verse's Effect Specifiers

Pure nodes have no execution pins and recompute on every use. Verse writes "pure or not" into the function signature: what computes, transacts, decides and suspends each govern.

Open the extra →

Technique · EXTRA

Indentation Style vs Brace Style: Two Layouts for the Same Verse

The same code can be written with a colon and indentation, or with braces and semicolons. When to use which, the one-line if (X) {} idiom, and how to settle on a house rule.

Open the extra →