Overview
A class in Verse is a composite type: a named blueprint that groups together fields (data) and methods (functions that operate on that data). When your game needs to track multiple things of the same kind — say, three dock gates each with their own open/closed state — a class lets you describe that shape once and stamp out as many instances as you need.
Use a class when:
- You have related data that belongs together (e.g., a gate's name, its trigger device, and whether it is locked).
- You want to reuse behavior across multiple objects without copy-pasting logic.
- You need inheritance — a specialized dock-alarm that extends a general alarm base.
Classes in Verse are declared with := class, can inherit from a parent with class(ParentClass), and are instantiated with curly-brace constructors. Every creative_device you write is itself a class that inherits from creative_device.
API Reference
(API surface could not be resolved for this device.)
Walkthrough
Scenario: A sunny cove has three wooden dock gates. Each gate is guarded by a trigger_device pressure plate on the pier. When a player steps on a plate, the matching gate's Verse class records the event, prints a harbor log message, and fires the trigger so downstream devices (like a barrier) can react. The dock_gate class bundles the gate's name and its trigger together — no parallel arrays, no confusion.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# ─── Custom class: one dock gate ─────────────────────────────────────────────
# A dock_gate bundles a human-readable name and the trigger_device
# that represents its pressure plate on the pier.
dock_gate := class:
# The display name shown in harbor logs (e.g. "North Gate").
Name : string = "Unnamed Gate"
# The pressure-plate trigger placed on the dock in the UEFN editor.
# NOTE: trigger_device fields inside a plain class are NOT @editable —
# only fields on a creative_device can carry @editable.
# We store a reference passed in at construction time instead.
Trigger : trigger_device
# Called when a player steps on this gate's plate.
# Fires the trigger so linked barrier devices react.
Activate(Agent : agent) : void =
Trigger.Trigger(Agent) # real trigger_device API: Trigger(Agent:agent):void
# ─── The island manager device ────────────────────────────────────────────────
# Place ONE of these in your level. Wire up the three trigger_device
# pressure plates via the @editable fields below.
dock_gate_manager := class(creative_device):
# Three pressure-plate triggers placed on the pier in the editor.
@editable
NorthPlateTrigger : trigger_device = trigger_device{}
@editable
EastPlateTrigger : trigger_device = trigger_device{}
@editable
SouthPlateTrigger : trigger_device = trigger_device{}
# We keep the three dock_gate instances as fields so handlers can reach them.
var NorthGate : dock_gate = dock_gate{ Name := "North Gate", Trigger := trigger_device{} }
var EastGate : dock_gate = dock_gate{ Name := "East Gate", Trigger := trigger_device{} }
var SouthGate : dock_gate = dock_gate{ Name := "South Gate", Trigger := trigger_device{} }
OnBegin<override>()<suspends> : void =
# Build the class instances now that @editable fields are populated.
set NorthGate = dock_gate{ Name := "North Gate", Trigger := NorthPlateTrigger }
set EastGate = dock_gate{ Name := "East Gate", Trigger := EastPlateTrigger }
set SouthGate = dock_gate{ Name := "South Gate", Trigger := SouthPlateTrigger }
# Subscribe to each plate's TriggeredEvent.
# TriggeredEvent is listenable(?agent) — handler receives ?agent.
NorthPlateTrigger.TriggeredEvent.Subscribe(OnNorthStepped)
EastPlateTrigger.TriggeredEvent.Subscribe(OnEastStepped)
SouthPlateTrigger.TriggeredEvent.Subscribe(OnSouthStepped)
# ── Event handlers (one per gate) ────────────────────────────────────────
# Each handler unwraps the optional agent before calling Activate.
OnNorthStepped(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
NorthGate.Activate(A)
OnEastStepped(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
EastGate.Activate(A)
OnSouthStepped(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
SouthGate.Activate(A)
Line-by-line highlights:
| Lines | What's happening |
|---|---|
dock_gate := class: |
Declares a plain (non-device) class. No creative_device parent needed — it's a pure data+behavior bundle. |
Name : string |
An immutable field with a default value. Overridden at construction time with Name := "North Gate". |
Trigger : trigger_device |
Stores a reference to a real placed device, passed in at construction. |
Activate(Agent : agent) |
A method on the class. Calls Trigger.Trigger(Agent) — the real trigger_device API. |
dock_gate{ Name := ..., Trigger := ... } |
Curly-brace constructor syntax. Every field you want to override is listed here. |
set NorthGate = ... |
Reassigns the var field after @editable devices are ready (they aren't available before OnBegin). |
TriggeredEvent.Subscribe(OnNorthStepped) |
Wires the plate's event to a class-scope handler method. |
if (A := MaybeAgent?): |
Safely unwraps ?agent — required because TriggeredEvent sends ?agent, not agent. |
Common patterns
Pattern 1 — Class inheritance: a specialized alarm gate
Extend dock_gate to create a alarm_gate that also fires a second trigger (a siren device) when activated.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# Base class
dock_gate := class:
Name : string = "Unnamed Gate"
Trigger : trigger_device
Activate(Agent : agent) : void =
Trigger.Trigger(Agent)
# Subclass — adds a siren trigger fired on every activation
alarm_gate := class(dock_gate):
SirenTrigger : trigger_device
# Override Activate to also fire the siren.
Activate<override>(Agent : agent) : void =
Trigger.Trigger(Agent) # parent behavior: open the gate
SirenTrigger.Trigger(Agent) # extra behavior: sound the siren
# Device that wires everything up
alarm_gate_demo := class(creative_device):
@editable
GateTrigger : trigger_device = trigger_device{}
@editable
SirenTrigger : trigger_device = trigger_device{}
@editable
PlateTrigger : trigger_device = trigger_device{}
var Gate : alarm_gate = alarm_gate{
Name := "Alarm Gate",
Trigger := trigger_device{},
SirenTrigger := trigger_device{}
}
OnBegin<override>()<suspends> : void =
set Gate = alarm_gate{
Name := "Alarm Gate",
Trigger := GateTrigger,
SirenTrigger := SirenTrigger
}
PlateTrigger.TriggeredEvent.Subscribe(OnPlateTriggered)
OnPlateTriggered(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
Gate.Activate(A) # calls the overridden Activate — fires both triggers
Pattern 2 — Triggering without an agent (code-driven activation)
Sometimes you want to open a gate on a timer, not because a player stepped on it. trigger_device exposes Trigger() (no agent) for exactly this.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# A timed gate that auto-opens after a delay — no player required.
timed_gate := class:
Name : string = "Timed Gate"
Trigger : trigger_device
DelaySeconds : float = 5.0
# Opens the gate after DelaySeconds. No agent needed.
OpenAfterDelay()<suspends> : void =
Sleep(DelaySeconds)
Trigger.Trigger() # trigger_device API: Trigger():void (no agent overload)
timed_gate_demo := class(creative_device):
@editable
HarborGateTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
# Build the timed gate class instance.
Gate := timed_gate{
Name := "Harbor Gate",
Trigger := HarborGateTrigger,
DelaySeconds := 8.0
}
# Suspend here until the gate opens — island sunrise moment!
Gate.OpenAfterDelay()
Pattern 3 — Array of class instances (managing a whole fleet of gates)
Store multiple dock_gate objects in an array and iterate over them to subscribe all plates at once.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
dock_gate := class:
Name : string = "Gate"
Trigger : trigger_device
Activate(Agent : agent) : void =
Trigger.Trigger(Agent)
fleet_gate_manager := class(creative_device):
@editable
Gate1Trigger : trigger_device = trigger_device{}
@editable
Gate2Trigger : trigger_device = trigger_device{}
@editable
Gate3Trigger : trigger_device = trigger_device{}
# Populated in OnBegin once @editable fields are live.
var Gates : []dock_gate = array{}
OnBegin<override>()<suspends> : void =
set Gates = array{
dock_gate{ Name := "Gate 1", Trigger := Gate1Trigger },
dock_gate{ Name := "Gate 2", Trigger := Gate2Trigger },
dock_gate{ Name := "Gate 3", Trigger := Gate3Trigger }
}
# Subscribe every gate's trigger to the same handler.
for (Gate : Gates):
Gate.Trigger.TriggeredEvent.Subscribe(OnAnyGateTriggered)
OnAnyGateTriggered(MaybeAgent : ?agent) : void =
# Fire ALL gates when ANY plate is stepped on — a chain reaction!
if (A := MaybeAgent?):
for (Gate : Gates):
Gate.Activate(A)
Gotchas
1. @editable only works on creative_device fields
Only fields declared directly on a class(creative_device) can carry @editable. If you put @editable on a field inside a plain class like dock_gate, the compiler will error. Pass device references in via the constructor instead.
2. trigger_device{} is a placeholder, not a real device
When you write var Gate : dock_gate = dock_gate{ Trigger := trigger_device{} } as a field initializer, that trigger_device{} is an empty shell — it has no placement in the world. Always reassign with the real @editable reference inside OnBegin.
3. TriggeredEvent sends ?agent, not agent
trigger_device.TriggeredEvent is listenable(?agent). Your handler must accept ?agent and unwrap it:
OnStepped(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# safe to use A as agent here
Forgetting the ? causes a type mismatch compile error.
4. Fields are immutable by default — use var to mutate
A class field declared without var cannot be reassigned after construction. If your game logic needs to update a field (e.g., tracking whether a gate is open), declare it var:
var IsOpen : logic = false
5. <suspends> propagates up the call chain
If a class method calls Sleep() or any other suspending function, it must be marked <suspends>. Any caller of that method must also be <suspends>. Plan your async boundaries before writing the class hierarchy.
6. Class instances are reference types
Assigning one dock_gate variable to another does not copy the object — both variables point to the same instance. Mutations through one variable are visible through the other. If you need independent copies, construct a fresh instance with dock_gate{ ... }.