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_deviceand ahud_message_deviceshare no common ancestor you can usefully program against, but you can make your own wrapper classes implement a shareddock_activatableinterface 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:
- The
trigger_devicefires (triggering linked confetti/cannon props). - The
hud_message_deviceflashes "⚓ Dock Secured!" to that player. - The
trigger_deviceis 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.