Verse Wiki — the interactive handbook for Verse developers
REFERENCE · HANDBOOK

Glossary: 50 Words That Unlock Verse

Reading the docs, browsing forums, staring at an error — you keep bumping into unfamiliar words. This page sorts 50 core Verse terms into five themed groups: the term itself, a plain-language explanation in two or three sentences, and a portal straight to the lesson that covers it. Use it as a dictionary — look it up, jump off.

1. Basics & Modules

What the language is, where code lives, how words get in — everything starts here.

Verse · the language

The programming language Epic built for the Unreal Engine and UEFN ecosystem — a "functional logic language": everything is an expression, failure replaces booleans, and concurrency primitives grow right out of the language itself. Conceived by Tim Sweeney over a decade and brought to life with Haskell luminaries like Simon Peyton Jones, it has been battle-tested on tens of thousands of Fortnite islands since 2023.

The programming language Epic built for the Unreal Engine and UEFN ecosystem — it does the same job as wiring nodes in Blueprint, just typed out instead. It has three temperaments Blueprint doesn't: everything evaluates to a value (even Branch-style checks have one); it rarely deals in True/False, preferring to ask "does this wire go through"; and a whole family of run-several-wires-at-once tricks (spawn, race and friends) is built into the language, no extra plugin needed. Conceived by Tim Sweeney over a decade, landed with Haskell luminaries, and battle-tested on tens of thousands of Fortnite islands since 2023.

Lesson 1 · What Verse Is →

UEFN · Unreal Editor for Fortnite

Unreal Editor for Fortnite — Epic's creation environment for Fortnite, and the stage your Verse code runs on. The workflow: create a .verse file in Verse Explorer, write code in VS Code, come back to the editor for Build Verse Code, then launch a session to see it live.

Lesson 1 · What Verse Is →

expression · everything has a value

Verse has no "statements" — everything is an expression and everything has a value: if has a value, for evaluates to an array, and a code block's value is its last expression. This is the master key to implicit returns, using if in place of the ternary operator, and a whole raft of other features.

In Blueprint, some nodes just "do a thing" (execution nodes) while others "spit out a value" (nodes with data pins). Verse erases that line: there is nothing here that only acts without producing a value — everything you write evaluates to something. Even a check (if) yields a value, and a loop (for) yields a whole array once it finishes. That's why functions need no dedicated Return node (the last value computed automatically becomes the result), and why you can use if directly as a pick-one-of-two without hunting for a ternary question mark like elsewhere.

Lesson 8 · if & Failure Contexts →

module · the unit of code

The atomic unit of code that can be redistributed and depended on. Every folder in a project automatically becomes a module of the same name, and you can define one explicitly with MyModule := module:; members default to internal, so cross-module use requires <public> on both the module and the member.

A bundle of code that can be packaged on its own and depended on by others — think "enabling a plugin" or referencing assets from another folder in Blueprint. Every folder in your project automatically becomes a module with the same name; what's inside is family-only by default, and for other folders to reference it, both the module and its members need the <public> tag (like flagging an asset as publicly referenceable).

Lesson 3 · Comments & Modules →

using · the import

using { /Fortnite.com/Devices } invites a module's public members into the current file — the braces are mandatory. The path looks like a URL: the domain reflects who owns the API, and a Temporary segment in the path marks an API that will move house someday.

using { /Fortnite.com/Devices } means "pull that drawer open first" — it invites a module's public members into the current file so you can actually use them; the braces are mandatory. That path looks like a URL: the domain says who the API belongs to, and a Temporary segment in the path is the heads-up that "this API will move house someday".

Lesson 3 · Comments & Modules →

implicit return · no Return node needed

The value of the last expression in a function body is the return value — no return keyword required. You may still return explicitly for an early exit; but inside a failure context (such as a <decides> function body) return is banned — a hard rule at the language level.

The last value computed in a function body automatically becomes the Blueprint function's return value — no dragging out a dedicated Return node and wiring the value into it. Want to bail out mid-way? An explicit return works too; but in a place that "might not go through" (the body of a failable <decides> function) return is banned — a rule written into the language.

Lesson 15 · Functions →

effect / specifier · the angle-bracket labels

A specifier is a semantic annotation in angle brackets, like <suspends>, <decides>, <override>, <public>, enforced by the compiler. The "effect" specifiers describe what a function may do: exactly one of transacts / varies / computes / converges, with decides and suspends stackable on top; annotate nothing and the default is no_rollback — irreversible, hence barred from failure contexts.

A specifier is a little label written in angle brackets, like <suspends>, <decides>, <override>, <public> — hit Compile, and the compiler checks your code against these labels. The "effect" family of labels declares "what this function will do": whether it can roll back, whether it reads or writes data, and so on — exactly one of transacts / varies / computes / converges, with failable (decides) and waiting (suspends) stackable on top; write nothing and the default is "cannot roll back", which bars it from checks that might not go through.

Lesson 18 · Specifiers & Attributes →

2. Types

From logic to tuple, every Verse type has its own temperament — learn the faces before making friends.

logic · the boolean

Verse's boolean type, holding only true and false. But it's not what if eats — conditions want a failable expression, so a logic needs the query operator to get in: if (Flag?).

It's Blueprint's Boolean — only true and false. But careful: Verse's if is not a Branch that takes a Boolean straight in — it asks "does this go through". To use a logic as a condition, append a question mark: if (Flag?), translating "true / false" into "goes through / doesn't".

Lesson 7 · logic & enum →

int / float / rational · the number trio

int is a 64-bit signed integer; float is an IEEE double-precision float whose literals must carry a decimal point (write 1.0, not 1); the two never convert implicitly — mixing them is a straight compile error. int divided by int yields a third type, rational (an exact fraction) — use Floor or Ceil to get back to int.

Two kinds of numbers: int is a whole number (Blueprint's Integer pin), float is a decimal (the Float pin). Decimals must be written with the point (write 1.0, not 1) or they're read as integers. The two don't auto-convert for you the way Blueprint does — mix them in one calculation and it lights up red. Bonus trivia: integer ÷ integer yields a third, exact-fraction type called rational; to get back to a whole number use Floor (round down) or Ceil (round up).

Lesson 5 · int & float →

string · text

Under the hood, a type alias for []char; literals use double quotes. Braces interpolate: "Score: {Score}" — escape a literal brace as \{. Being an array, the whole array toolkit — Length, indexing, Slice — applies verbatim.

A run of text in double quotes (Blueprint's String). To embed a variable's value in the text, use braces: "Score: {Score}" — whatever's inside gets swapped for the actual value (like wiring a variable into a Format Text placeholder); to print an actual brace, escape it as \{. Under the hood it's an array of characters, so the array toolkit (length, index, slice) works on it as-is.

Lesson 6 · Strings & Interpolation →

enum · named options

A set of named values: card_suit := enum{Clubs, Diamonds, Hearts, Spades}, accessed as card_suit.Clubs. Members hide no integers underneath — no converting to int or string; branching goes to the case expression.

A set of well-named options — the Enumeration asset you create from the right-click menu in Blueprint, say "Clubs / Diamonds / Hearts / Spades". Pick one as "type name.member name" (like choosing from the enum dropdown). No integer hides behind a member — no converting to numbers or text; to handle each value separately, hand it to case (your Switch on Enum).

Lesson 7 · logic & enum →

array · the ordered list

An ordered list of same-typed elements: type []int, literal array{1, 2, 3}. Indexing starts at 0 and is a failable expression — out of bounds doesn't crash, it just fails; the array itself is an immutable value, so "modifying" really means replacing the whole thing.

A row of same-typed things in order — Blueprint's Array. Take items by index (counting from 0), but that step "might not go through" — an out-of-range index won't crash the game; the step just fails and takes the else. One temperament Blueprint doesn't have: Verse arrays are "read-only values", so "changing the array" actually means swapping the whole pack for a new one. Process them one by one with ForEach.

Lesson 12 · Arrays →

map · the lookup table

A key-value register: type [string]int, literal map{"a" => 1}. Keys must be subtypes of comparable; reads and writes are both failable — even set M[K] = V has to sit inside an if.

A "key → value" register (Blueprint's Map): look up a score by name, health by ID. Both lookups and writes "might not go through" — looking up a missing key fails — so even writing set M[K] = V has to sit inside an if. Whatever type you use as the key must come from the "can be compared for equality" family.

Lesson 13 · Maps →

weak_map · the save-game map

map's special cousin: no iteration, no Length — only per-key reads and writes. Module-scope vars must be weak_map types; a module-level weak_map keyed by player with persistable values is exactly the vehicle for cross-session saves.

A special relative of map: it can't be iterated as a whole and won't tell you its size — you read and write one key at a time. Its headline job is saving: put it "outside the class" (module level), key it by player, declare the value persistable, and you've got a per-player SaveGame that's still there after quitting and rejoining.

Lesson 22 · Persistent Data →

option · the maybe-box

The "might have a value, might be empty" box: the type is written ?int, the empty value is false, and values go in with option{42} (braces, not parentheses). Unwrap with the postfix ?, a failable expression; and option{...} is itself a failure context that turns failure into an empty box.

A little box that "might hold something, might be empty". The type is written ?int, the empty box is false, and things go in with option{42} (note the braces). To take the contents out, append a question mark — a step that "might not go through" (an empty box takes the else). And option{...} is itself a fault-tolerant spot: if the step inside fails, it doesn't error — it just hands you an empty box.

Lesson 14 · option & tuple →

tuple · the fixed bundle

A fixed-length, mixed-type group of values: type tuple(int, string), literal (1, "a"). Elements read with parentheses T(0) — the arity is settled at compile time, so access cannot fail, but the index must be a compile-time constant. Multiple return values ride on it.

A fixed few values, possibly of different types, bundled into one little pack — say "an integer + a piece of text". Pack with parentheses (1, "a"), and take the nth item with parentheses too: T(0) — how many items and each one's type are locked in at compile time, so reading can't fail, but the index has to be a hardcoded constant. When a Blueprint function wants to hand back several return values at once, this is how.

Lesson 14 · option & tuple →

class · data + behavior

A composite structure with reference semantics: my_class := class: defines fields and methods, with Self referring to the current instance inside method bodies. Single inheritance from one class plus any number of interfaces; overriding a member requires <override>, and the parent implementation is called as (super:)Method().

The Blueprint class itself — data (fields) and behavior (methods) packed into something you can stamp instances from over and over, with Self meaning "this instance of me" inside methods. It can claim only one parent class (inheriting like a Child Blueprint Class) but can sign up for several interfaces at once; overriding an inherited member must carry <override> (that Override dropdown in Blueprint), and calling the parent's original implementation is (super:)Method().

Lesson 16 · Classes & Inheritance →

archetype · instantiation, Verse-style

How Verse instantiates objects: there is no new — write my_class{Field := 3} directly, and the braces must assign every field that lacks a default. For more elaborate initialization logic, a <constructor> function is available.

How you "build an instance" in Verse: no new keyword like elsewhere — write my_class{Field := 3} and out one comes, with every field lacking a default filled in inside the braces (a bit like filling in the required fields in the Details panel before you can Spawn). To wrap up fancier initialization steps, there's also an optional <constructor> function.

Lesson 16 · Classes & Inheritance →

struct · plain data, copied by value

A value-semantics aggregate of pure data: assignment, parameter passing, and container storage all copy the whole thing. No var fields, no methods in the body (add behavior via extension methods); a regular at small data bundles and persistence.

The Structure asset from your right-click menu — a small bundle of pure data. It has "value" semantics: assign it to someone, pass it as a parameter, drop it in a container — each time the whole pack is copied (editing the copy never touches the original). House rules: no mutable var fields inside, no methods either (add behavior with extension methods); a regular for small data bundles and save data.

Lesson 17 · struct & interface →

interface · the contract

A behavioral contract of pure function signatures — no fields, no implementations. A class can implement several at once, providing each interface method with <override>; polymorphism across inheritance trees rides on it.

You know this one already — it's the Blueprint Interface (BPI): it only lists "which functions must exist", never how; whoever signs up for the interface has to fill those functions in themselves (each with <override>). Verse's interface is BPI in code form. One class can sign up for several at once, and you get one uniform way to call across different inheritance trees.

Lesson 17 · struct & interface →

comparable · allowed to ask "equal?"

The family of types that can be tested for equality with = and <>: int, float, logic, string, char, enums, plus any array / map / option / tuple whose elements are all comparable. Map keys must be subtypes of comparable — the admission check at the door.

The "can be tested for equality" family of types: integers, floats, Booleans, text, characters, enums, plus arrays / maps / options / tuples whose contents are all comparable. Why care? Because a map's key must come from this family — it's the entry ticket for being a key. Equal is =, not-equal is <>.

Lesson 13 · Maps →

unique · instances with identity

A class specifier: every instance of a <unique> class receives a unique identity at construction and compares by identity with =, which makes it comparable — this is precisely why player and agent can be map keys. Ordinary class instances have no identity and cannot be compared at all.

A label you hang on a class: <unique>. With it, every instance that rolls off the line carries a "one-of-a-kind ID card", so you can check by identity whether two are the same one — which also qualifies them as map keys. Players (player, agent) can be keys for exactly this reason. Ordinary instances without the label have no ID card and can't be compared at all.

Lesson 16 · Classes & Inheritance →

3. Flow & Failure

"Failure" is Verse's most distinctive invention: not an accident, but control flow with first-class citizenship.

failure context · where failure is allowed

The positions where failable expressions may run: if conditions, for filter clauses, the operands of not and or, <decides> function bodies, and option{...} initializer blocks. Write a failable expression anywhere else and the compiler bounces it straight back.

The designated spots where "checks that might not go through" are allowed: an if's condition parentheses, the value and filter clauses of a for / ForEach, the operands of not and or, the body of a failable function (one with <decides>), and an option{...} initializer block. Shove a "might fail" check anywhere outside these spots, and Compile stops you at the door with an error.

Lesson 8 · if & Failure Contexts →

failable expression · might not go through

An expression that either succeeds with a value or fails: comparisons, array and map indexing, square-bracket calls Foo[], the query X?, and type casts all qualify. Failure is not an exception — it's normal control flow; the program strolls quietly into the else.

An operation that "either succeeds with a value or doesn't go through": size / equality comparisons, taking elements by index or key, square-bracket calls to failable functions Foo[], the postfix query X?, and type casts. Casts you know best — that's Blueprint's Cast node: one wire on success, and that red Cast Failed pin on failure. Failure isn't an error and nothing crashes — it's the perfectly normal other way out, and the program strolls quietly into the else.

Lesson 8 · if & Failure Contexts →

decides · the failable effect

The effect specifier that turns a function into a failable one: calls switch to square brackets Foo[Args], and the call site must itself sit inside a failure context. It must appear paired with <transacts> — failure has to be able to roll back.

Hang it on a function and that Blueprint function becomes one that "might not go through": calls switch to square brackets Foo[Args] (the brackets are the "careful, this step can fail" marker), and it can only be called where failure is allowed. It must appear together with <transacts> — if it can fail, everything done before the failure has to be erasable in one stroke.

Lesson 18 · Specifiers & Attributes →

transacts · the rollback effect

Declares that the function's actions can roll back as a whole: reads, writes, allocations — all undoable. Failure contexts only admit functions with a rollbackable effect; a function without it defaults to no_rollback, and dropping one into an if condition earns the classic error "effects that are not allowed by its context".

The function's declaration that "everything I do can be undone": reads, writes, spawning things — all reversible. Why it matters: "places that allow failure" only admit functions that can take it all back; a function without this label defaults to "spilled water can't be unspilled", and the moment you tuck one into an if condition you hit the classic error "effects that are not allowed by its context".

Lesson 18 · Specifiers & Attributes →

speculative execution / rollback · try first, undo on failure

The execution strategy of failure contexts: assume success and run ahead; the moment it fails, retract every effect from the attempt (set modifications included) wholesale, as if it never happened. This is a language-level transaction mechanism — the literal meaning of Epic's line that Verse "transactionalizes C++".

How a "place that allows failure" actually runs: Verse boldly wires the whole thing up on a shadow graph first and gives it a try; if the check ultimately doesn't go through, every change made along the way (including variables changed through Set nodes) is thrown away, as if those nodes were never connected. It's the language's built-in undo potion — no plugin required; Epic baked this "try the wiring, and if it fails, pretend it never happened" transactional power straight into the language.

Lesson 8 · if & Failure Contexts →

query operator ? · true means go

The postfix question mark, one symbol with two jobs: on a logic value, true succeeds and false fails; on an option, non-empty unwraps and empty fails. Both uses are failable expressions and neither survives outside a failure context.

One postfix question mark, two jobs: behind a logic (Boolean), true goes through and false doesn't; behind an option (the box), a filled box gets cracked open and an empty one doesn't go through. Both uses "might not go through", so both can only sit where failure is allowed.

Lesson 7 · logic & enum →

set · the assignment keyword

Verse's iron three-way split: := defines, set ... = assigns, and a bare = is a failable equality comparison (== simply doesn't exist). Skip the set and the compiler reads your "assignment" as a comparison — with an error message that's hard to parse.

To "give a variable a new value", use set — your Set node in Blueprint. Don't mix up the three symbols here: := defines the first time, set ... = changes the value afterwards (wiring a Set), and a lone = is actually an equality check (and there's no == at all). Forget the set and the compiler treats your "change" as a "compare" — with a rather cryptic error to boot.

Lesson 4 · Constants & Variables →

for · the bounded loop

The iteration expression over finite collections: for (X : Arr), indexed for (I -> X : Arr), ranged for (I := 0..10). The clauses in the parentheses form an independent failure context each round — a failed filter only skips that round; the whole for evaluates to an array, a list comprehension out of the box.

It's the ForEach Loop: run through a collection one item at a time — for (X : Arr) takes each element, for (I -> X : Arr) brings the index along, for (I := 0..10) runs a numeric range. The conditions in the parentheses are per-round "might-fail" checks, each round judged on its own — a round that doesn't pass just gets skipped. And the whole for accumulates a new array when it finishes, so it was born for "collecting as you go".

Lesson 10 · for Loops →

loop / break · repeat until break

loop repeats unconditionally; the only exits are break or return — Verse has no while and no continue. A synchronous loop that never breaks triggers ErrRuntime_InfiniteLoop and halts Verse device-wide; the async game loop's standard kit is loop plus Sleep.

loop is a wire that circles back on itself and repeats unconditionally (a While Loop whose condition is forever True); the only ways out are break or return — Verse has no standalone while and no continue. The deadly part: a synchronous loop that neither breaks nor takes a breather triggers the infinite-loop error ErrRuntime_InfiniteLoop, and every bit of Verse on the island goes on strike; hence the game loop's standard kit of a loop with a Delay (Sleep) inside.

Lesson 11 · loop & defer →

defer · run on the way out

The "run when leaving the current scope" cleanup block: once control flow has passed the defer and registered it, it runs no matter which exit is taken; leaving before reaching the defer doesn't count. Paired operations — open/close, show/hide — are safest cleaned up here.

"The one wire guaranteed a final run right before leaving this stretch." Once control flow has passed the defer and checked it in, the wrap-up runs no matter which exit you leave through; bail out before ever reaching the defer, and it doesn't count. Open/close doors, show/hide UI — for these "has a beginning, needs an end" paired operations, handing the cleanup to defer is the safest bet.

Lesson 11 · loop & defer →

4. Concurrency & the Flow of Time

Verse treats "time" as a language concept on par with control flow: these words are the if and for of the time dimension.

Verse treats "time" as a proper language concept too, right up there with branches and loops: think of this group of words as "Branch and ForEach in the time dimension" — the same splitting up and stepping through, only across several timelines running at once.

immediate / async expression · now vs. across frames

Verse splits expressions along the time axis: immediate expressions finish within the current tick; async expressions (Sleep, Await, concurrency blocks) may span multiple simulation updates. Failure contexts admit immediate expressions only — something suspended across frames can't be rolled back.

Verse sorts actions by "does it take time": immediate actions finish on the spot within this frame (tick); async actions (Delay/Sleep, waiting on events, concurrency blocks) may take several frames to complete. "Places that allow failure" only admit immediate actions — if something hangs in mid-air waiting for frames, there's no clean way to strike it all out and roll back.

Lesson 19 · suspends & the Flow of Time →

suspends · the async license

A function's async license: a <suspends> function may suspend across multiple simulation updates, yielding control cooperatively. It can only be called directly from an async context; synchronous code has to send spawn to start it.

The function's "async license": a <suspends> function has waiting nodes in its body (Blueprint's latent nodes with the little clock, like Delay) and can hang suspended across frames, politely handing control back in the meantime. Such a function can only be called directly from a "context that can also wait"; to start one from plain synchronous code, spawn does the honors.

Lesson 19 · suspends & the Flow of Time →

Sleep · the Delay node

Sleep(Seconds:float)<suspends>:void, from /Verse.org/Simulation: suspends the current coroutine for the given seconds — it sleeps only itself, never the game. Sleep(0.0) waits exactly one frame; Sleep(Inf) waits forever, existing solely to be canceled.

It's the Delay node: Sleep(Seconds:float) parks the current wire in place for that many seconds — only this wire waits; the game keeps running just fine. Sleep(0.0) waits exactly one frame (the go-to for "continue next frame"); Sleep(Inf) waits forever, there purely to be canceled by someone else.

Lesson 19 · suspends & the Flow of Time →

simulation update · the tick

Synonymous with tick and frame: the smallest unit of Verse's flow of time, about 30 per second on Fortnite servers. Sleep's precision bottoms out here — timing finer than one tick does not exist.

Lesson 19 · suspends & the Flow of Time →

spawn · fork and forget

spawn{ Fn() } launches an independently running async task and continues immediately — the braces take exactly one async function call. It is the only unstructured concurrency expression and the only one allowed in synchronous contexts: async work inside a Subscribe callback rides entirely on it.

spawn{ Fn() } forks a new wire off the current line to run on its own, without waiting — it moves right along (the braces take exactly one async function call). It's the only "nobody manages its lifetime" flavor of concurrency, and the only one usable in synchronous code — so doing anything that waits inside a Subscribe (a bound event callback) leans entirely on it to open the way.

Lesson 20 · Concurrency Expressions →

sync · wait for everyone

Runs all async subexpressions in the block concurrently and continues only when every one has finished; results come back packed into a tuple in written order. In one line: dinner starts when everyone's at the table.

Several wires run together, and things only move on once every one of them has arrived — dinner starts when everyone's at the table. Each wire's result comes back packed into a little bundle (tuple) in written order.

Lesson 20 · Concurrency Expressions →

race · first one wins, rest canceled

Runs the branches concurrently; the first to finish wins, the rest are auto-canceled at their next suspension point, and the race's value is the winner's result. The standard shape of "timeout logic": one arm works, one arm Sleeps.

Several wires race at once; whoever crosses the line first takes it, the rest are cut on the spot at their next waiting point, and what race hands back is the champion's result. It's the standard way to write a "timeout": one wire does the real work, another runs a Delay as the clock — first one home calls it.

Lesson 20 · Concurrency Expressions →

rush · first one wins, rest finish

Like race, takes the first finisher's result — but the losers aren't canceled and keep running until the enclosing async scope ends. Use it for "first come first served, but everyone's work gets done".

Takes the first wire's result just like race; the difference: the stragglers aren't cut — they keep running until the stretch they live in comes to an end. For "first come first served, but everyone finishes their chores", use this one.

Lesson 20 · Concurrency Expressions →

branch · fork with a chaperone

Starts an async block while the following code continues immediately; when the enclosing async scope exits, the branch's task is automatically retired. Like spawn, but with a managed lifetime — exactly why it out-recommends spawn in the official guidance.

Forks a wire off to run while the main line continues immediately; when the stretch the main line lives in ends, the forked wire gets cleaned up automatically. Just like spawn, but with someone minding the lifetime for you — which is exactly why Epic recommends it over bare spawn.

Lesson 20 · Concurrency Expressions →

task · the handle to a running job

spawn's return value: task(t) represents an async computation in flight, offering Await() to wait for completion and collect the result. Note it has no public Cancel method — the idiom for canceling a spawned task is to have the function race a cancellation event internally.

The "handle" to the wire spawn forked off: hold it and you can Await() for that wire to finish and take the result back. Note there's no ready-made Cancel button on it — to call off a spawned wire midway, the idiom is to have the function race a "cancel event" internally.

Lesson 20 · Concurrency Expressions →

structured concurrency · scoped lifetimes

The shared creed of the sync / race / rush / branch four: a task's lifetime is bounded by its enclosing scope — scope exits, tasks auto-cancel, no orphan tasks left behind. The official guiding principle: if structured works, don't spawn.

The shared creed of the four brothers sync / race / rush / branch: any wire forked off has its lifespan managed by the stretch it lives in — the stretch ends, and the wires still running inside get cleaned up automatically, never leaving an unsupervised "orphan wire" behind. Epic's advice: if one of these four fits, don't bare-spawn.

Lesson 20 · Concurrency Expressions →

event · the signal

event(t) is Verse's built-in signaler: one end fires Signal(Payload), the other end suspends on Await() or registers a callback with Subscribe. Declare it as a "type = archetype" pair: MyEvent : event(agent) = event(agent){}; the workhorse of decoupled device-to-device communication.

event(t) is the Event Dispatcher: Signal(Payload) on one end is your Call (broadcast once, data attached), and on the other end Await() suspends until it rings, or Subscribe registers a callback (the Bind). Declare it as a "type = archetype" pair. When devices need to talk across the map without hard-wiring each other, this is the workhorse.

Lesson 21 · Devices & Events →

5. Devices & Data

Code eventually lands in the level: devices are Verse's body in the game world, and data is its memory.

creative_device · the device base class

The base class for custom Verse devices, from /Fortnite.com/Devices: my_device := class(creative_device):. After a successful compile, the device must still be dragged from the Content Browser into the level to run; each instance runs its own copy of the code.

The "base Blueprint class" for the devices you build yourself; writing my_device := class(creative_device): inherits from it. Once it compiles, you still have to drag the device from the Content Browser into the level before it actually runs; drag in as many as you like — each copy runs its own code (same idea as placing multiple device instances).

Lesson 2 · Your First Device →

OnBegin / OnEnd · lifecycle entry points

The device's opening and closing acts: OnBegin<override>()<suspends>:void runs when the game experience starts — the long-lived coroutine that carries the game loop; OnEnd runs when the experience ends — with Epic's explicit warning that tasks spawned inside OnEnd may never get a chance to run.

The device's opening and closing acts. OnBegin is Event BeginPlay: it runs the moment the game experience starts, and it's usually where you hang the entire game loop (which is why it's a long-lived wire that can wait); OnEnd runs when the experience ends — with Epic's explicit warning not to count on anything you spawn inside OnEnd ever getting to run.

Lesson 2 · Your First Device →

@editable · exposed to the panel

An @ attribute written on the line above a field, exposing it to the UEFN Details panel: tweaking values and wiring device references need no recompile. The field must have a default, with device references initialized from an empty archetype: Button : button_device = button_device{} — code all correct but the panel wiring forgotten is beginner trap number one.

It's the Instance Editable eyeball on a variable: it exposes the field to UEFN's Details panel, so once the device is in the level you tweak values and wire device references right in the panel — no recompiling. The requirement: the field needs a default value, and device references get an empty placeholder first: Button : button_device = button_device{}. Code all correct but the wire never connected in the panel — beginner trap number one.

Lesson 21 · Devices & Events →

agent / player · who did it

The engine's two levels of identity: agent is the abstraction for "something that can trigger behavior", and player is its subtype (a real human player). Device event payloads are mostly agent; convert with the failure-context cast if (P := player[Agent]), and reach the character entity with Agent.GetFortCharacter[].

The engine's two layers of "who did it": agent is the abstraction for "someone who can trigger behavior" (think the Instigator pin on events), and player is its more specific kind (a real human player). What device events hand you is mostly an agent; to treat it as a player, do a conversion in a "place that allows failure": if (P := player[Agent]) (just like Cast to Player), and to reach the character itself, Agent.GetFortCharacter[].

Lesson 21 · Devices & Events →

Subscribe / cancelable · bind and unbind

Callback-style listening on device events: Button.InteractedWithEvent.Subscribe(OnPressed), with the callback signature matching the event payload. Subscribe returns a cancelable — call Cancel() to unsubscribe; and subscribe exactly once — repeated subscriptions stack callbacks, so one interaction fires N times.

Hanging a callback on a device event — Blueprint's Bind Event: Button.InteractedWithEvent.Subscribe(OnPressed), with the callback's parameters matching what the event delivers. Subscribe hands back a cancelable handle; call its Cancel() to unbind. And remember to bind only once — repeated bindings stack, and a single press fires the callback several times over.

Lesson 21 · Devices & Events →

persistable · save-file eligibility

A type's "save-file credentials": primitive types qualify from birth; custom classes must be written class<final><persistable> with constant fields only. Add a module-level var X : weak_map(player, t) = map{} and the data saves itself per player across sessions — leaderboards, coins, progress all start here.

A type's "good enough for the save file" credential: basic types qualify from birth; a custom class must be written class<final><persistable> with nothing but immutable fields inside. Pair it with a player-keyed weak_map(player, t) outside the class (module level), and the data keeps one copy per player, automatically still there after quitting and rejoining — leaderboards, coins, progress all start here (it's the Verse edition of SaveGame).

Lesson 22 · Persistent Data →