Verse Wiki — the Verse handbook for Blueprint authors
Chapter 3 · Lesson 8
Constants & Variables: Name Tags and Backpack Slots
Every variable you create in the Blueprint variables panel is one line of code in Verse. Chapter 3 starts with data, and this lesson hands out two pieces of starter gear: a name tag that gets engraved once and never swapped (constants), and a backpack slot whose contents you can swap anytime (variables, var). Learn how to put a type label on your data, how to create it with :=, how to change it with set (that's the Set node from Blueprints), then learn to recognize the two most common Compile errors — and your variables panel has moved into code.
1. Constants: Hang the Name Tag, Never Swap It
Game data comes in two temperaments. One kind should never change from match start to finish — the arena's name, the max health, the seconds per round. The other changes constantly — gold count, score, ammo remaining. Verse issues a separate piece of gear for each. First up: constants.
The full way to declare a constant is Name:type = value, in three parts: the name is the text engraved on the tag, :type dictates which kind of value this tag may pair with, and = value is the content engraved on the spot. Note — no extra keyword whatsoever. In Verse, immutability is the default treatment. You don't have to apply for it.
arena_device.verse
using { /Fortnite.com/Devices }
arena_device := class(creative_device):
# Full form: Name:type = value
MaxHealth:int = 100
ArenaName:string = "Rookie Arena"
# Shorthand: := lets Verse infer the type
# 12 is an integer literal, so int is inferred
MaxPlayers := 12
Blueprint translation: this is like opening the arena_device Blueprint's variables panel and creating three variables — MaxHealth (type Integer, default 100), ArenaName (type String, default "Rookie Arena"), and MaxPlayers (type Integer, default 12). All three are constants: meaning not a single Set node anywhere in the whole Blueprint ever touches them. The := syntax is "create the variable and fill in its default value" in one step — and Verse picks the Variable Type field for you, based on that default.
The third example uses the shorthand :=: name and initial value in one go, no need to fill in the type yourself — Verse guesses it from the value on the right. 12 is an integer, so MaxPlayers is an integer (Integer in the Blueprint variables panel). Look familiar? The arena_device := class(creative_device): we've been writing for the past few lessons is just hanging a name tag on a Blueprint class — the exact same syntax as hanging one on a number.
Constants come with two iron rules. First, constants declared inside functions and modules must be given an initial value on the spot — there is no "hang an empty tag now, engrave it later" (class fields are the exception — skip the default, and whoever drops this Blueprint into the level has to fill it in on the Details panel; more on that when we cover classes). Second, once engraved, it's welded shut: wire a Set node into a constant and a red error lights up the moment you hit Compile (you'll see what it looks like in Section 4). Rule of thumb: write it as a constant first, add var to upgrade it only when it truly needs to change — let the values that can stay fixed stay fixed, and anyone opening your Blueprint can tell at a glance what's set in stone and what's live.
2. Variables (var): A Backpack Slot Only set Can Swap
Now for the data that changes. Every character carries a backpack: one slot for potions, one for gold. Programs need the same kind of "slot" for data that changes — that's a variable. Opening a slot takes one extra word in front of the declaration: var.
var Health:int = 100 does four things at once: var is the application to "open a mutable slot"; Health is the label on the slot; :int says the slot only holds integers; = 100 is the starter item it ships with. Just like constants, variables declared in functions and modules must be given an initial value — Verse doesn't let empty slots leave the factory (the class-field exception from the previous section applies here too). In the official docs, var declarations always spell the type out in full (just like the Variable Type field has to be set when you create a Blueprint variable), and this tutorial follows suit: variables exist to be modified over and over, so labeling clearly what they hold keeps whoever is modifying them on solid ground.
The slot is open — how do you swap what's inside? This is where Verse shows the most personality: you must first call out the magic word set.
backpack_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
backpack_device := class(creative_device):
# var: open a mutable slot, starting at 100
var Health:int = 100
OnBegin<override>()<suspends>:void =
set Health = 80
set Health -= 30
set Health += 5
Print("Health: {Health}")
set Health = 80 replaces the whole thing; set Health -= 30 and set Health += 5 subtract from and add to the current value (there's *= for multiplying, too). After all three lines, the slot holds 55. In Blueprint terms, that's three chained Set nodes, changing Health to 80, then subtracting 30, then adding 5. Why is Verse so strict about changing values? Because mutating state is a disaster zone for bugs: force yourself to wire in a Set node for every change, and you can never "mis-wire one connection and silently change a variable without realizing it". Pitfalls you'd stare at a debugger over for hours elsewhere simply cannot be dug in Verse — leave out one set, and Compile blocks you before the code ever gets a chance to run.
3. Line by Line: Declare → set → Print
Put a constant and a variable in the same device and watch each do its job: the name tag never moves, the backpack slot gets Set twice, and finally Print String puts the result on screen. That {RoomName} / {GoldCoins} business is called "interpolation" — the variable name inside the curly braces is swapped for its current value, like wiring variables into a Print String's text pin and stitching the pieces together. Click "Run Next Step".
loot_room_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
loot_room_device := class(creative_device):
# Constant: a name tag, hung once and never swapped
RoomName:string = "Treasure Room"
# Variable: a backpack slot whose contents can change
var GoldCoins:int = 0
OnBegin<override>()<suspends>:void =
set GoldCoins = 30
set GoldCoins += 20
Print("{RoomName} gold: {GoldCoins}")
keyOld
Output Log
Click "Run Next Step" to watch the code execute line by line.
Look at lines 13 and 14: same slot, contents swapped twice. Meanwhile nobody ever touched RoomName — and nobody could. That's this lesson's entire worldview: what changes gets a new variable and Set nodes to change it; what doesn't gets a name tag and never gets a Set wired in.
4. Common Pitfalls: Three Symbols, Two Errors
This lesson introduced three symbols that look like close relatives — blink and you'll grab the wrong one. Cheat sheet first:
Syntax
Purpose
Example
:=
Declare and initialize (hang a tag / open a slot)
MaxPlayers := 12
set … =
Modify a variable (only works on var)
set Score = 100
=
Compare the two sides for equality
Score = 100 (a question, not an assignment!)
Error one: forgetting set. Wanted to change a value and just wrote Score = 100? Verse has no == at all — the single ='s whole job is "comparison", and it's a "check that might not pass" (equal counts as passing, unequal doesn't; you'll meet it properly when we cover Branch-style checks). So when you hit Compile, the error won't kindly say "you forgot set" — it throws you a line no newcomer can parse, roughly: this check that might not pass must sit somewhere that is allowed to not pass. From now on, whenever "failure context" pops up in a Compile error, first go back and check whether a set went missing.
Error two: wiring a Set into a constant. Use set MaxHealth = 50 on a name declared without var, and it gets rejected outright the moment you hit Compile — the left side isn't a variable at all; there is no slot to swap. This error, at least, is refreshingly direct, and it's really prompting you to make a design decision: does this value truly need to change? If yes, go back to the declaration and add var (turning it into a variable that accepts Set); if not, delete that set line.
One more for the road: new variables need a default. Writing just var Score:int without filling in an initial value won't pass Compile either — variables created in functions and in modules must be given a default on the spot. The "potion shop" below has had two keywords dug out of it. Fill them back in:
potion_shop_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
potion_shop_device := class(creative_device):
# Constant: potion price, fixed for the whole match
PotionPrice:int = 25
# Stock will change — which keyword opens a mutable slot?
____ Stock:int = 10
OnBegin<override>()<suspends>:void =
# Sell one bottle — what magic word comes before changing a slot?
____ Stock -= 1
Print("Price {PotionPrice}, stock {Stock}")
5. Scope 101: Leave the Code Block, Lose the Name
One last question: once a name tag is hung, where does it count? Answer: it follows the "code block". Verse uses indentation to circle code off into blocks — the deeper the indent, the smaller the circle: the whole Blueprint class is the biggest circle, a function graph is a smaller circle inside it, and the bodies of Branch and ForEach (coming in later lessons) are smaller circles still. Just like a Local Variable in Blueprints is only recognized inside its own function graph, a constant or variable created in some circle in Verse is only recognized in that circle (and the smaller circles inside it); the moment that circle ends, the name is void.
scope_demo_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
scope_demo_device := class(creative_device):
OnBegin<override>()<suspends>:void =
Reward:int = 100
if (Reward > 50):
# Bonus is declared inside the if block
Bonus:int = 10
Print("Usable inside the block: {Bonus}")
# Bonus has expired by this point
# Print("{Bonus}") ← uncomment this for a compile error
(This is just a first hello to if — it's the Branch check from Blueprints, covered properly in a later chapter. For now, all you need to know: the indented lines after its colon are one circled-off little block.) Blueprint translation: Reward is created in the function's circle; Bonus is only created on the branch where the Branch check passes, so it's only recognized inside that little circle. Try to use Bonus outside the circle, and Compile says it doesn't recognize the name. The fix is humble: either move the nodes that use it into that circle, or create Bonus in the circle outside.
Verse also has one house rule of its own: no shadowing. Many languages let you create an inner variable with the same name as an outer one, and the inner one quietly "covers up" the outer — a classic bug nursery. Verse cuts it off flat: if the outer circle already has Count, creating another Count := 7 inside is a straight-up Compile error. The fix is to pick a different name, so every name tag is one of a kind.
One teaser to close: peel the scope onion to its outermost layer and you reach the "module" (think of it as a whole asset folder). You may already be scheming — "I'll put a var TotalKills:int = 0 at the very top as a global counter". Sadly, hitting Compile will get you an error: running Verse in UEFN today, module-level mutable variables come with one special requirement (they must be weak_map types — something like "one SaveGame per player, still there after leaving and rejoining"). Why that rule exists — and how it hands you cross-session saves on the side — is in the extras at the end of this page.
Blueprint Cross-Reference
Everything in this lesson is something you have already done in the Blueprint variables panel. Put the two side by side — the differences column is the part worth memorizing.
In Blueprints
In Verse
Difference
Variables panel → new variable, never wired to a Set node
MaxHealth:int = 100
Blueprint variables are mutable by birth; Verse flips it — immutable is the default, mutable is what you apply for
Drag the variable into the graph and get a Get node
Just write the name: MaxHealth
Verse has no read action at all — the name is the value, and it costs you no node
Set node (wire a new value in)
set Health = 80
Blueprints use a wire, Verse uses a keyword; drop the set and Compile reads the line as a comparison and stops you
The Variable Type dropdown in the variables panel
The type after the colon: :int / :string / :logic
:= lets Verse pick the type from the value on the right, skipping the field entirely
Filling in Default Value on the Details panel
The = 100 on the same line as the declaration
Verse keeps default and declaration together; constants and variables created inside a function must be given a value on the spot
Ticking Instance Editable (the eye next to the variable)
@editable
Same effect: editable on the Details panel once dropped into a level. Verse writes it as an attribute line, not a checkbox
Creating a Local Variable inside a function graph
var Count:int = 0 inside the function body
Same reach — this function only; but Verse adds one rule: it may not share a name with an outer one (no shadowing)
Every difference points at the same thing. Blueprints made the variable a piece of furniture: you place it on a panel, then spend a node to read it and a node to write it. Verse made the variable a line of text: the name is the read, set is the write, and type and default share the same line. Losing the node as an intermediary costs you something — every single change has to be typed out by hand — and buys you something: open a .verse file and you can see in one pass what is fixed and what is live.
One asymmetry is worth flagging: Blueprints have no concept of an immutable variable. The closest thing is your own promise not to wire a Set node into it, and no compiler ever holds you to that promise. Verse promotes the promise into the type system, which is why the whole family of bugs that starts with "something changed a value it shouldn't have" simply fails to compile here.
6. Level Challenge
Backpack sorted — time to clear three mini-challenges and appraise the loot. Correct answers earn a star ★; wrong answers can be retried forever, zero penalty.
You want to declare a max health value that never changes for the whole game. Which one is the Verse constant?
You wrote Score = 100 (with no set in front). How will the compiler read this line?
A constant Bonus is created inside the little circle of a Branch (Blueprint's if check). Can it still be used outside that circle?
A variable in a class can be "anyone reads, only the class wires in Set" — one little setting guards who can read, another guards who can change it, and you get an externally read-only variable without a single extra node.
Module-Scoped Variables & Data Persistence: weak_map and persistable
Why can't you create a global mutable variable at the outermost layer? The special rule for module-level variables — and the "still there after leaving and rejoining" cross-session save system it throws in for free.
Constants first, changes only through an explicit Set, = as comparison rather than assignment — every one of these "quirks" traces back to a single paper: the Verse Calculus.