Verse Wiki — the Verse handbook for Blueprint authors
Chapter 6 · Lesson 20

Classes & Inheritance: Drawing Blueprints for Your Game Objects

So far, all your code has lived inside a single device. This lesson hands you the drafting pen: define your own types with class, stamp out instances with archetype syntax, and let subclasses stand on their parents' shoulders through inheritance — in fact, every device you've written since Lesson 6 has been using it all along.

1. class: One Blueprint, Countless Copies

Say your level needs 20 monsters: each one has a name, a health pool, and knows how to take damage. Write a separate set of variables and functions for every single monster and your code explodes on the spot. The programmer's move: draw a blueprint first — one that spells out what a monster looks like and what it can do — then mass-produce from the drawing. That blueprint is a class.

monster.verse
using { /UnrealEngine.com/Temporary/Diagnostics }

# A class: type names use lower_snake_case (just like creative_device)
monster := class:

    # Constant field: no default value, must be supplied at instantiation
    Name:string

    # Mutable field: declared with var, default value 100
    var Health:int = 100

    # Method: a function living in the class body — every monster carries it
    TakeDamage(Amount:int):void =
        set Health -= Amount
        Print("{Name} takes {Amount} damage")

Read through the drawing above: monster contains two kinds of things — variables (data) and blueprint functions (behavior). The variables are the same kind you create in the Variables panel: Name is a String, deliberately given no default value, and since it has no var (think of it as a variable that never gets a Set node wired to it), it's a constant — locked in the moment the instance is built, never changeable. So every monster produced from this drawing must be named on the spot, or Compile goes straight to red. Health is an Integer, defaults to 100, and carries var, meaning you can change it later with a Set node. That bottom line, set Health -= Amount, is simply a Set node that subtracts Amount from Health.

TakeDamage is a function graph that ships with this blueprint (the f-icon kind in the My Blueprint panel). Drag Health into that function graph and you always get the health bar of the current instance — as if the function's default Target were the instance itself; to spell it out, write Self.Health, which is like explicitly wiring a Self reference into the Target pin. As for type names in lower_snake_case and variable/function names in PascalCase — that's just a naming convention in code; the engine accepts whatever names you use in the Blueprint panels, so a quick glance is enough.

2. Archetype Instantiation: Mass-Producing Instances from the Drawing

The blueprint is drawn — how do you actually build a real monster from it? In Verse the recipe is short: write the class name followed directly by a pair of curly braces { }, and inside them fill in the fields you want to customize using := (read it as "set the right side as its initial value"). It's just like dragging a Blueprint Class into the level and filling in its initial values item by item in the Details panel — except here you write it out as text.

Slime := monster{Name := "Slime"} — doesn't that look like filling out a factory spec sheet? There's only one rule, but it's iron: every field without a default value must appear inside the braces — not one may be missing, or Compile goes straight to red. Fields with defaults (like Health) can be left out to take their default, or written in to override it on the spot. Hit "Run Next Step" and watch two monsters walk out of the same blueprint:

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

monster := class:
    Name:string
    var Health:int = 100

    TakeDamage(Amount:int):void =
        set Health -= Amount
        Print("{Name} takes {Amount} damage, {Health} HP left")

arena_device := class(creative_device):

    OnBegin<override>()<suspends>:void =
        Slime := monster{Name := "Slime"}
        Boss := monster{Name := "Dragon", Health := 500}
        Slime.TakeDamage(30)
        Boss.TakeDamage(120)
Output Log

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

Those last two lines deserve a second look: Slime.TakeDamage(30) and Boss.TakeDamage(120) call the same method, yet the Slime drops to 70 and the Dragon to 380 — every instance keeps its own independent copy of each field. That's the core value of a class: write the logic once, let each keep its own data.

3. Inheritance & <override>: Subclasses Stand on Their Parents' Shoulders

As your level grows, the monsters split into species: goblins, dragons, ghosts… they all have names and health, but each brings its own flavor. Copy the common parts three times? No — keep the common parts in one "base class" blueprint and let subclasses inherit it. The syntax: a pair of parentheses after class, with the parent's name inside:

goblin.verse
using { /UnrealEngine.com/Temporary/Diagnostics }

# Base class: the shared blueprint for all enemies
enemy := class:
    Name:string = "Nameless Enemy"
    var Health:int = 100

    Describe():void =
        Print("{Name} appears!")

# Inheritance: goblin automatically gets all of enemy's fields and methods
goblin := class(enemy):

    # Overriding a field's default value also requires <override>
    Name<override>:string = "Goblin"

    # Overriding a method: <override> is mandatory
    Describe<override>():void =
        Print("{Name} charges at you, waving a little wooden club and shrieking!")

Translating the drawing: the top half, enemy, is the parent blueprint, holding two variables — Name and Health — plus a Describe function graph. The bottom half, goblin := class(enemy), means "create a Child Blueprint Class based on enemy" — the goblin inherits all three automatically, nothing needs redrawing. The goblin makes exactly two changes: it swaps Name's default to "Goblin" and replaces the Describe function graph with its own version; both names carry <override>, the exact equivalent of the Override dropdown in a Blueprint — telling the engine loud and clear, "I know the parent has this; I mean to stomp it."

Three rules, and every one is exam material. First: after goblin := class(enemy):, the goblin already owns Name, Health, and Describe without writing a thing — inheritance pays out in full. Second: to change anything the parent gave you (a method implementation or a field default), you must append <override> to the name, explicitly declaring "I know the parent has this, and I'm changing it on purpose"; leave it off and Compile goes red, complaining the parent already has this member. Third: a class can inherit one base class only — to wear several "identities" at once, you'll need next lesson's interface.

Two more advanced bits to file away for now: hanging <abstract> on a blueprint marks it as a pure drawing that can "only be a parent, never take the stage" — you can't build instances from it directly (it can't be placed in a level), it can only be inherited by other blueprints, which makes it perfect as a shared parent; also, if you want an Overridden function to run the parent's original routine too, the Blueprint equivalent is right-clicking and choosing "Add Call to Parent Function" — the Verse spelling looks very strange, (super:), with enough pitfalls to deserve its own article; see the extras at the end.

Now look back at the line you've written for over a dozen lessons: my_device := class(creative_device): — of course! Every device you've built is a subclass inheriting the engine base class creative_device; and the <override> in OnBegin<override>()<suspends>:void is you overriding the parent's "called when the game starts" method. This lesson taught you nothing new — it just took the machinery you've been using all along and opened it up for you to see.

Strike while the iron's hot — complete the subclass below: one blank takes "the base class to inherit", the other takes "the angle-bracket tag you must hang on when Overriding":

ghost.verse
using { /UnrealEngine.com/Temporary/Diagnostics }

# Base class: the shared blueprint for all enemies
enemy := class:
    Name:string = "Nameless Enemy"

    Describe():void =
        Print("{Name} appears!")

# Subclass: the ghost. To inherit from enemy, what goes in the parentheses?
ghost := class(____):

    Describe<____>():void =
        Print("A cold wind blows, and a ghost drifts out...")

4. Composition over Inheritance: Think "Has-A" Before "Is-A"

In the first week after learning inheritance, everyone tries to cram the whole world into one inheritance tree: enemy begets flying_enemy, which begets flying_fire_enemy… Three levels in, you discover the "fire-breathing treasure chest" has no branch to hang from. The seasoned move is to ask first: is this relationship really "is-a", or is it "has-a"? A chest is not a kind of enemy — it merely has a health bar. So don't inherit: make the health bar its own small blueprint and stuff it in as a variable. This trick is called "composition":

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

# A reusable "ability": the health bar
health_bar := class:
    var Current:int = 100

    Damage(Amount:int):void =
        set Current -= Amount

# A chest HAS a health bar (has-a) — it is not a kind of enemy (is-a)
chest := class:
    Loot:string = "Gold Coins x 100"
    HP:health_bar

loot_device := class(creative_device):

    OnBegin<override>()<suspends>:void =
        # Assembly: install the health bar into the chest at instantiation
        MyChest := chest{HP := health_bar{}}
        MyChest.HP.Damage(25)
        Print("Chest HP: {MyChest.HP.Current}")

Translating the drawing: first build a small reusable blueprint, health_bar, containing just one Current variable (default 100) and one Damage function graph (a Set node subtracting the incoming Amount from Current). The chest chest doesn't inherit from enemy; instead it declares a variable HP whose type is health_bar — effectively stuffing a "health bar component" into the chest blueprint. When an actual chest gets built, MyChest := chest{HP := health_bar{}} first builds a health bar, then installs it in the chest; after that, MyChest.HP.Damage(25) means "grab the health bar on this chest and run its Damage." If the castle gate or a statue wants a health bar too, give each its own health_bar variable the same way — none of them interfere.

The payoff of composition is immediate: the same health_bar can be installed in chests, castle gates, and statues at once — whoever needs a health bar gets one, with zero regard for where they sit in any inheritance tree; want to swap in shield logic instead, just replace the field's type without shaking the whole structure. Rule of thumb: save inheritance for true "is-a" relationships (a goblin is an enemy; your device is a creative_device) — everywhere else, reach for composition first. Inheritance is a strong but rigid steel frame; composition is flexible Lego — and most of the time, what you need is Lego.

5. Classic Pitfalls: Compile's Four Favorite Red Errors

▢ Pitfall 1: forgetting <override> on an override. Write a method in the subclass with the same name as the parent's, and Compile goes red, complaining the parent already has this member — it can't tell whether you meant to override or fat-fingered a duplicate name, so you must declare intent with <override>. The reverse holds too: hang <override> on a member that doesn't exist in the parent, and that errors as well.

▢ Pitfall 2: missing a required field in the archetype. monster{} looks wonderfully lazy, but as long as Name has no default, that line fails Compile and goes straight to red — every field without a default value must be assigned in the braces, no exceptions.

▢ Pitfall 3: trying to build an instance straight from an abstract class. A class hung with <abstract> is a pure drawing; writing enemy_base{} sends Compile straight to red — an abstract blueprint can only be a parent, so you have to build from one of its concrete subclasses instead.

▢ Pitfall 4: assuming that assigning an object to a new name copies it. Blueprint objects are references — just like an Object Reference variable in a Blueprint: assigning the reference A to B doesn't clone a second monster; B is merely a second address plate on the same one. Drain HP through B, and the change shows when you look through A

reference_trap.verse
using { /UnrealEngine.com/Temporary/Diagnostics }

A := monster{Name := "Slime"}
B := A             # No copy! B and A point at the same monster
B.TakeDamage(30)   # HP lost through B...
Print("{A.Health}") # ...is gone when viewed through A too: prints 70

Modify a var field through any one reference and every other reference sees it. Want the "assigning really does clone an independent copy" behavior? That's next lesson's headliner: struct.

Blueprint Cross-Reference

This lesson has no new concepts, really — every step you take in the Content Browser and Class Settings has a matching line of code here. The difference column is the one that matters.

In Blueprint In Verse Difference
Content Browser → right-click → Blueprint Class, then pick a parent in the dialog goblin := class(enemy): One line of text replaces an asset creation plus a dialog pick; the parent goes in the parentheses, and no parentheses at all means a bare class with no parent
Add a variable in the variables panel, set its type and default in Details A field in the class body, var Health:int = 100 Verse is immutable by default: without var, a field is locked the moment it is built; a field with no default must be supplied at instantiation
My Blueprint → the Override dropdown, to override a parent event or function Describe<override>():void = The dropdown can only list what is overridable; in Verse you declare intent yourself with <override> — omit it and you get "member already defined in the parent", invent one and you get "the parent has no such member"
Right-click inside an overridden function → Add Call to Parent Function (the Parent node) (super:)Describe() Blueprint gives it to you on a right-click; the Verse spelling looks strange, and super only ever means the immediate parent — you cannot reach further up the chain. See this lesson's extras
Drag the Blueprint into the level, or Spawn Actor from Class Archetype syntax, monster{Name := "Slime"} What goes in the braces is exactly what you type into the Details panel; the difference is that no field without a default may be skipped — miss one and Compile goes red on the spot, rather than leaving an empty pin to blow up at runtime
Tick Abstract in Class Settings base := class<abstract>: Same effect (cannot be instantiated, parent duty only); the switch just moved from a settings panel into the angle brackets after class
A Blueprint can pick only one parent class Verse likewise inherits from one base class only Identical on both sides. To wear several "identities" at once, Blueprint uses Implemented Interfaces and Verse uses next lesson's interface
Add Component, hanging a component on an Actor Another class held as a field, HP:health_bar Blueprint components have their lifetime managed by the engine and show up in the viewport; Verse "composition" is just an ordinary field — building it and installing it in the archetype is on you
An Object Reference variable (assigning A to B) B := A Reference semantics on both sides: B is merely a second address plate on the same object, so draining HP through B shows through A too. Want "assignment really copies"? That's next lesson's struct

The differences all cluster in one place: Blueprint keeps the "shape of a class" in panels; Verse keeps it in one piece of text. Parent class, Abstract, variable types, defaults, which functions are overridden — in Blueprint those are scattered across Class Settings, the variables panel, the Details panel and the Override dropdown, and you window-hop to assemble the full picture. In Verse they all sit next to the names, readable in one screenful. The price is that no dropdown enumerates "here is what you may override" for you; you lean on editor autocomplete and the docs instead.

The second difference bites harder: Blueprint lets you create the asset first and leave pins empty, saving the explosion for runtime. Verse moves that whole class of problem forward to Compile. A required field skipped in an archetype, a missing <override> — neither waits until you press Play. It grates at first; once it is habit, you notice it is catching exactly the bugs that were hardest to track down in Blueprint.

Level Challenge 🏁

Blueprint drawn — time for the inspection stamp. Three questions, zero penalty for wrong answers, retry as often as you like.

In Verse, which of these builds a monster instance?

monster's Name field has no default value. What happens when you write M := monster{}?

A subclass wants to override the parent's Describe() method. What's the right way?

Further Reading

Technique · EXTRA

(super:) — The Easiest Call to Get Wrong in Verse

After overriding a method, how do you call the parent's implementation back? A syntax that looks deeply weird — learn it once and you're immune for life.

Enter the extra →

Deep Dive · EXTRA

unique: Object Identity and the Secret of comparable

Why can't two instances even be compared with = by default? And what earns player the right to be a map key? The answer is one specifier.

Enter the extra →

Bonus · EXTRA

constructor vs Archetype: Two Ways to Build Objects

Beyond filling in curly braces, Verse also has <constructor> functions — when should you use which?

Enter the extra →