Reference Verse compiles

interfaces: Polymorphic Contracts for Island Devices

Verse interfaces let you define a shared contract — a set of methods every implementor must provide — so one piece of code can drive wildly different objects without caring what they are under the hood. On a sun-drenched pirate lagoon island, that means a single 'activate the dock mechanism' routine can work whether it's firing a trigger, flashing a HUD message, or lighting up a button highlight, all without a forest of if/else branches. Master interfaces and your island code becomes modular, te

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

Overview

An interface in Verse is a named contract: it lists method signatures (and optionally default implementations) that every class claiming to implement it must honour. The compiler enforces the contract at build time, so you get a compile error — not a runtime crash — if you forget a method.

Why reach for interfaces on your UEFN island?

  • Polymorphism without inheritance chains. A trigger_device and a hud_message_device share no common ancestor you can usefully program against, but you can make your own wrapper classes implement a shared dock_activatable interface and treat them identically.
  • Decoupled systems. Your game-manager device doesn't need to know which device it's talking to — only that it satisfies the interface.
  • Testability. Swap a real device wrapper for a stub that also implements the interface during iteration.

The canonical lagoon scenario used throughout this article: a pirate dock has three mechanisms — a pressure plate (trigger_device), a glowing HUD banner (hud_message_device), and a chest button (button widget). When the player steps onto the dock, all three must activate in sequence. An interface ties them together.

API Reference

trigger_device

Used to relay events to other linked devices.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from trigger_base_device.

trigger_device<public> := class<concrete><final>(trigger_base_device):

Events (subscribe a handler to react):

Event Signature Description
TriggeredEvent TriggeredEvent<public>:listenable(?agent) Signaled when an agent triggers this device. Sends the agent that used this device. Returns false if no agent triggered the action (ex: it was triggered through code).

Methods (call these to make the device act):

Method Signature Description
Trigger Trigger<public>(Agent:agent):void Triggers this device with Agent being passed as the agent that triggered the action. Use an agent reference when this device is setup to require one (for instance, you want to trigger the device only with a particular agent.
Trigger Trigger<public>():void Triggers this device, causing it to activate its TriggeredEvent event.
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
SetMaxTriggerCount SetMaxTriggerCount<public>(MaxCount:int):void Sets the maximum amount of times this device can trigger. * 0 can be used to indicate no limit on trigger count. * MaxCount is clamped between [0,20].
GetMaxTriggerCount GetMaxTriggerCount<public>()<transacts>:int Gets the maximum amount of times this device can trigger. * 0 indicates no limit on trigger count.
GetTriggerCountRemaining GetTriggerCountRemaining<public>()<transacts>:int Returns the number of times that this device can still be triggered before hitting GetMaxTriggerCount. Returns 0 if GetMaxTriggerCount is unlimited.
SetResetDelay SetResetDelay<public>(Time:float):void Sets the time (in seconds) after triggering, before the device can be triggered again (if MaxTrigger count allows).
GetResetDelay GetResetDelay<public>()<transacts>:float Gets the time (in seconds) before the device can be triggered again (if MaxTrigger count allows).
SetTransmitDelay SetTransmitDelay<public>(Time:float):void Sets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.
GetTransmitDelay GetTransmitDelay<public>()<transacts>:float Gets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.

button

Button is a container of a single child widget slot and fires the OnClick event when the button is clicked.

Full public surface, resolved verbatim from the live Epic digest (UnrealEngine.digest.verse). Inherited members are merged from widget.

button<native><public> := class<final>(widget):

Events (subscribe a handler to react):

Event Signature Description
OnClick OnClick<public>():listenable(widget_message) Subscribable event that fires when the button is clicked.
HighlightEvent HighlightEvent<public>():listenable(widget_message)
UnhighlightEvent UnhighlightEvent<public>():listenable(widget_message)

hud_message_device

Used to show custom HUD messages to one or more agents.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

hud_message_device<public> := class<concrete><final>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
ShowMessageEvent ShowMessageEvent<public>:listenable(agent) Called when a Message has been Shown on-screen. Returns an Agent if it was Shown on a specified Agent's screen.
HideMessageEvent HideMessageEvent<public>:listenable(agent) Called when a Message has been Hidden on-screen. Returns an Agent if it was Hidden from a specified Agent's screen.
ClearAllMessagesEvent ClearAllMessagesEvent<public>:listenable(agent) Called when all queued Messages from all players that are affected by this HUD Message Device have been cleared.

Methods (call these to make the device act):

Method Signature Description
Show Show<public>(Agent:agent):void Shows the currently set HUD Message on Agents screen. Will replace any previously active message. Use this when the device is setup to target specific agents.
Show Show<public>():void Shows the currently set Message HUD message on screen. Will replace any previously active message.
Hide Hide<public>():void Hides the HUD message.
Hide Hide<public>(Agent:agent):void Hides the currently set HUD Message on Agents screen. Use this when the device is setup to target specific agents.
Show Show<public>(Agent:agent, Message:message, ?DisplayTime:float Displays a Custom message to a specific Agent that you define.Setting DisplayTime to 0.0 will display the HUD message persistently.If not defined, or less than 0.0 the message will show for the time set on the device.
Show Show<public>(Message:message, ?DisplayTime:float Displays a Custom message that you define for all PlayersSetting DisplayTime to 0.0 will display the HUD message persistently.If not defined, or less than 0.0 the message will show for the time set on the device.
SetDisplayTime SetDisplayTime<public>(Time:float):void Sets the time (in seconds) the HUD message will be displayed. 0.0 will display the HUD message persistently.
GetDisplayTime GetDisplayTime<public>()<transacts>:float Returns the time (in seconds) for which the HUD message will be displayed. 0.0 means the message is displayed persistently.
SetText SetText<public>(Text:message):void Sets the Message to be displayed when the HUD message is activated. Text is clamped to 150 characters.
ClearAllMessages ClearAllMessages<public>():void Clears all queued Messages from all players that are affected by this HUD Message Device.

Walkthrough

Scenario: The Pirate Dock Awakens

A player swims to the lagoon dock and steps on a pressure plate. Three things should happen in order:

  1. The trigger_device fires (triggering linked confetti/cannon props).
  2. The hud_message_device flashes "⚓ Dock Secured!" to that player.
  3. The trigger_device is then disabled so it can't fire again until reset.

We model each mechanism as a class that implements a shared dock_activatable interface, then loop over them in one clean pass.

# dock_interface_manager.verse
# Pirate lagoon dock — interface-driven activation system

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# ─────────────────────────────────────────────
# 1. Define the interface contract
# ─────────────────────────────────────────────
dock_activatable := interface:
    # Called when the dock mechanism should fire.
    Activate(Agent : agent) : void
    # Called to shut the mechanism down after use.
    Deactivate() : void

# ─────────────────────────────────────────────
# 2. Wrapper: trigger_device activatable
# ─────────────────────────────────────────────
trigger_activatable := class(dock_activatable):
    Device : trigger_device

    Activate<override>(Agent : agent) : void =
        # Fire the trigger with the specific agent so linked devices
        # know which player caused the activation.
        Device.Trigger(Agent)

    Deactivate<override>() : void =
        # Prevent re-triggering until the game manager re-enables it.
        Device.Disable()

# ─────────────────────────────────────────────
# 3. Wrapper: hud_message_device activatable
# ─────────────────────────────────────────────
hud_activatable := class(dock_activatable):
    Device : hud_message_device

    # Localized helper so we can pass a message to Show()
    DockMessage<localizes>(S : string) : message = "{S}"

    Activate<override>(Agent : agent) : void =
        # Show a custom message to the specific agent for 4 seconds.
        Device.Show(Agent, DockMessage("⚓ Dock Secured!"), ?DisplayTime := 4.0)

    Deactivate<override>() : void =
        # Clear the banner when the mechanism winds down.
        Device.Hide()

# ─────────────────────────────────────────────
# 4. The creative_device that wires everything
# ─────────────────────────────────────────────
dock_interface_manager := class(creative_device):

    # Place a trigger_device on the dock pressure plate in UEFN
    @editable
    DockPlate : trigger_device = trigger_device{}

    # The trigger that fires the cannon/confetti props
    @editable
    CannonTrigger : trigger_device = trigger_device{}

    # HUD banner device
    @editable
    DockBanner : hud_message_device = hud_message_device{}

    OnBegin<override>()<suspends> : void =
        # Allow the dock plate to fire exactly once per round.
        DockPlate.SetMaxTriggerCount(1)
        # Subscribe: when a player steps on the plate, run OnDockReached
        DockPlate.TriggeredEvent.Subscribe(OnDockReached)

    # Handler — note the ?agent signature required by TriggeredEvent
    OnDockReached(MaybeAgent : ?agent) : void =
        if (Agent := MaybeAgent?):
            # Build the list of activatables — pure interface values
            Mechanisms : []dock_activatable = array:
                trigger_activatable{Device := CannonTrigger}
                hud_activatable{Device := DockBanner}

            # One loop activates every mechanism regardless of type
            for (Mechanism : Mechanisms):
                Mechanism.Activate(Agent)

            # Deactivate all mechanisms after a short beat
            # (In a real island you'd Sleep() here inside a task)
            for (Mechanism : Mechanisms):
                Mechanism.Deactivate()

Line-by-line explanation

Lines What's happening
dock_activatable := interface: Declares the contract. Any class that claims (dock_activatable) must implement both Activate and Deactivate.
trigger_activatable, hud_activatable Concrete wrapper classes. Each holds a reference to a real device and fulfils the interface by calling the device's own API.
Device.Trigger(Agent) Calls trigger_device.Trigger(Agent:agent) — the agent-aware overload so linked devices know who triggered it.
Device.Show(Agent, DockMessage(...), ?DisplayTime := 4.0) Calls the three-parameter hud_message_device.Show overload with a localized message and a named optional float.
Device.Disable() Calls trigger_device.Disable() to prevent re-triggering.
Device.Hide() Calls hud_message_device.Hide() to remove the banner.
DockPlate.SetMaxTriggerCount(1) Caps the plate at one trigger per round.
[]dock_activatable = array{...} The array holds interface values — the loop doesn't care which concrete type is inside.
MaybeAgent : ?agent / if (Agent := MaybeAgent?) TriggeredEvent sends ?agent; we unwrap safely before use.

Common patterns

Pattern 1 — Default interface implementation (free behaviour)

Interfaces can supply a default method body. Implementors inherit it for free and only override when they need custom behaviour. Here a resettable interface provides a default no-op Reset so simple wrappers don't have to write one.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Interface with a default implementation
resettable := interface:
    Reset() : void = {}   # default: do nothing
    Arm() : void          # abstract — must be overridden

# Only Arm() needs an override; Reset() is inherited for free
cannon_trigger_wrapper := class(resettable):
    Device : trigger_device

    Arm<override>() : void =
        # Re-enable the trigger and give it 3 more shots
        Device.Enable()
        Device.SetMaxTriggerCount(3)

# A wrapper that DOES need a custom reset
hud_wrapper := class(resettable):
    Device : hud_message_device

    Arm<override>() : void =
        Device.Show()

    Reset<override>() : void =
        # Custom reset: wipe every queued message
        Device.ClearAllMessages()

lagoon_reset_manager := class(creative_device):
    @editable
    CannonTrigger : trigger_device = trigger_device{}

    @editable
    BannerDevice : hud_message_device = hud_message_device{}

    OnBegin<override>()<suspends> : void =
        Mechanisms : []resettable = array:
            cannon_trigger_wrapper{Device := CannonTrigger}
            hud_wrapper{Device := BannerDevice}

        # Arm everything
        for (M : Mechanisms):
            M.Arm()

        # Later, reset everything — cannon uses default no-op,
        # hud_wrapper runs ClearAllMessages()
        for (M : Mechanisms):
            M.Reset()

Pattern 2 — Querying device state through an interface

Interfaces can expose read-only data members. Here a delay_queryable interface lets a manager ask any device wrapper for its current reset delay without knowing the concrete type.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Interface that surfaces a timing query
delay_queryable := interface:
    GetCurrentResetDelay() : float
    ApplyNewDelay(Seconds : float) : void

trigger_delay_wrapper := class(delay_queryable):
    Device : trigger_device

    GetCurrentResetDelay<override>() : float =
        # Calls the real trigger_device API
        Device.GetResetDelay()

    ApplyNewDelay<override>(Seconds : float) : void =
        Device.SetResetDelay(Seconds)

lagoon_timing_manager := class(creative_device):
    @editable
    WaveTrigger : trigger_device = trigger_device{}

    @editable
    TreasureTrigger : trigger_device = trigger_device{}

    @editable
    TimingBanner : hud_message_device = hud_message_device{}

    ResetDelayMessage<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        Wrappers : []delay_queryable = array:
            trigger_delay_wrapper{Device := WaveTrigger}
            trigger_delay_wrapper{Device := TreasureTrigger}

        # Double every device's reset delay and report it
        for (W : Wrappers):
            OldDelay : float = W.GetCurrentResetDelay()
            NewDelay : float = OldDelay * 2.0
            W.ApplyNewDelay(NewDelay)

        # Show a confirmation banner to all players
        TimingBanner.Show(ResetDelayMessage("Delays extended — harder mode!"), ?DisplayTime := 3.0)

Pattern 3 — Subscribing to events through an interface wrapper

Interfaces can also encapsulate event subscriptions, letting a manager react to any device event through a uniform Listen call.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# Interface for anything that can notify us when it fires
event_notifier := interface:
    Listen(OnFired : type { _(?agent) : void }) : void

trigger_notifier := class(event_notifier):
    Device : trigger_device

    Listen<override>(OnFired : type { _(?agent) : void }) : void =
        Device.TriggeredEvent.Subscribe(OnFired)

lagoon_event_manager := class(creative_device):
    @editable
    ShoreAlarmTrigger : trigger_device = trigger_device{}

    @editable
    AlarmBanner : hud_message_device = hud_message_device{}

    AlarmText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        Notifiers : []event_notifier = array:
            trigger_notifier{Device := ShoreAlarmTrigger}

        for (N : Notifiers):
            N.Listen(OnAlarmFired)

    OnAlarmFired(MaybeAgent : ?agent) : void =
        if (Agent := MaybeAgent?):
            AlarmBanner.Show(Agent, AlarmText("🚨 Shore alarm triggered!"), ?DisplayTime := 5.0)
            # Also transmit immediately — zero transmit delay
            ShoreAlarmTrigger.SetTransmitDelay(0.0)
        else:
            # Triggered by code, not a player — show global banner
            AlarmBanner.Show(AlarmText("🚨 Automated alarm!"), ?DisplayTime := 3.0)

Gotchas

1. <override> is mandatory on every interface method

If your class implements an interface method but forgets <override>, the compiler treats it as a new method that shadows the interface slot — and the class is rejected as not fully implementing the interface. Always write MethodName<override>(...).

2. Interface instances are not the same as creative_device fields

You cannot tag an interface field with @editable. The @editable attribute only works on concrete device types (e.g., trigger_device, hud_message_device). Always declare @editable fields as their concrete type, then wrap them in interface-implementing classes inside OnBegin.

3. TriggeredEvent sends ?agent, not agent

trigger_device.TriggeredEvent is a listenable(?agent). Your handler signature must be (MaybeAgent : ?agent) : void. Unwrap with if (Agent := MaybeAgent?) before passing Agent to any method that expects a plain agent (like hud_message_device.Show(Agent:agent, ...)). Skipping the unwrap is a type error.

4. message is not a string

hud_message_device.Show(Agent, Message:message, ...) and SetText(Text:message) both require a message value, not a raw string literal. Declare a localizes helper:

MyText<localizes>(S : string) : message = "{S}"

Then call MyText("your string here"). There is no StringToMessage function.

5. SetMaxTriggerCount clamps to [0, 20]

Passing a value above 20 silently clamps to 20. Pass 0 for unlimited. Plan your trigger budgets accordingly — if you need more than 20 activations, set to 0 and manage the count yourself in Verse.

6. Default interface implementations are inherited, not injected

If your class does not override a method that has a default body in the interface, the default runs. This is intentional — but if you accidentally misspell your override method name, the default silently runs instead of your custom code. The compiler won't warn you because the class still satisfies the interface. Double-check method names and <override> tags.

7. Interfaces cannot hold @editable or var mutable fields

Interface data members are read-only constants. If you need mutable state, put a var field on the implementing class, not on the interface itself.

Guides & scripts that use trigger_device

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

Build your own lesson with trigger_device

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 →