Reference Verse compiles

Defining Classes in Verse: Blueprints for Your Game Objects

Every interesting Fortnite island has moving parts — a harbor crane, a dockmaster's gate, a tide-bell that rings when someone steps on the pier. In Verse, a **class** is the template you use to bundle the data and behavior that make those parts tick. Learn to define classes and you unlock the full power of object-oriented design in UEFN.

Updated Examples verified on the live UEFN compiler
Watch the Knotdefine_a_class in ~90 seconds.

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{ ... }.

Guides & scripts that use define_a_class

Step-by-step tutorials that put this object to work.

Build your own lesson with define_a_class

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →