Overview
Verse is an indentation-sensitive language: the indent level of a line determines which block it belongs to. Get it wrong and your if body runs unconditionally, your for loop exits early, or your event handler never fires. Unlike curly-brace languages, there are no {} to save you — the whitespace is the grammar.
The two devices we use to make this concrete are:
trigger_device— a pressure plate or invisible zone that firesTriggeredEventwhen a player steps on it. You can also callTrigger()in code, set limits withSetMaxTriggerCount, and add delays withSetResetDelay.hud_message_device— shows a custom message on one or all players' screens. CallShow(Agent, Message)for a targeted pop-up,SetTextto change the message, andClearAllMessagesto wipe the slate.
When to reach for these together: Any time you need a spatial event (player walks somewhere) to communicate feedback ("You found the treasure!"). Classic uses: checkpoint announcements, puzzle hints, countdown warnings, and — as we'll build below — a pirate lagoon discovery sequence.
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. |
player
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse). Inherited members are merged from agent.
player<native><public> := class<unique><persistent><module_scoped_var_weak_map_key><epic_internal>(agent):
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
Scene: The Pirate Lagoon Discovery
Your cel-shaded island has three zones:
- The Dock — players start here.
- The Cove — a hidden inlet halfway along the shore.
- The Clifftop — a treasure chest sits at the top.
Three trigger_device pressure plates mark each zone. When a player steps on one, a hud_message_device flashes the zone name. The Clifftop trigger is one-shot (fires once per player session) and uses a reset delay so it can't be spammed. The Cove trigger broadcasts to all players.
This example demonstrates:
- Subscribing to
TriggeredEvent(alistenable(?agent)) and unwrapping the optional agent. - Calling
Show(Agent, Message)for targeted messages andShow(Message)for global ones. - Using
SetMaxTriggerCount,SetResetDelay, andSetTextat startup. - How indentation inside
if,OnBegin, and handler methods controls exactly what runs.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# Localized message helpers — message params MUST be declared like this.
DockMsg<localizes>(S : string) : message = "{S}"
CoveMsg<localizes>(S : string) : message = "{S}"
CliffMsg<localizes>(S : string) : message = "{S}"
pirate_lagoon_discovery := class(creative_device):
# ── Placed devices — wire these up in the UEFN editor ──
@editable
DockTrigger : trigger_device = trigger_device{}
@editable
CoveTrigger : trigger_device = trigger_device{}
@editable
ClifftopTrigger : trigger_device = trigger_device{}
@editable
HUD : hud_message_device = hud_message_device{}
# ── Lifecycle ──
OnBegin<override>()<suspends> : void =
# Configure the Clifftop trigger: one shot, 2-second reset.
# INDENTATION NOTE: these three calls are all inside OnBegin's block.
ClifftopTrigger.SetMaxTriggerCount(1)
ClifftopTrigger.SetResetDelay(2.0)
# Pre-load the HUD device's default text (shows for 4 seconds).
HUD.SetDisplayTime(4.0)
HUD.SetText(DockMsg("Welcome to the Pirate Lagoon!"))
# Subscribe handlers — each is a method defined below.
DockTrigger.TriggeredEvent.Subscribe(OnDockTriggered)
CoveTrigger.TriggeredEvent.Subscribe(OnCoveTriggered)
ClifftopTrigger.TriggeredEvent.Subscribe(OnClifftopTriggered)
# ── Event handlers ──
# TriggeredEvent sends ?agent — we MUST unwrap with if (A := Agent?).
OnDockTriggered(Agent : ?agent) : void =
# INDENTATION NOTE: the if-body is indented one level deeper.
if (A := Agent?):
# Only the triggering player sees this message.
HUD.Show(A, DockMsg("⚓ You're on the Dock."))
# This line is OUTSIDE the if — it always runs, even if no agent.
# (Useful for debug; remove in production.)
OnCoveTriggered(Agent : ?agent) : void =
# Broadcast to ALL players — no agent needed for Show().
# INDENTATION NOTE: both lines below are siblings in this method's block.
HUD.ClearAllMessages()
HUD.Show(CoveMsg("🏴☠️ Someone found the Secret Cove!"))
OnClifftopTriggered(Agent : ?agent) : void =
if (A := Agent?):
# Targeted message: only the discoverer sees this.
HUD.Show(A, CliffMsg("💀 TREASURE FOUND! You reached the Clifftop!"), ?DisplayTime := 6.0)
# Re-enable the trigger so the NEXT player can also trigger it once.
# INDENTATION NOTE: this is still inside the if-block — it only
# runs when a real agent triggered it, not a code-only trigger.
ClifftopTrigger.Enable()
Line-by-line highlights
| Lines | What's happening |
|---|---|
DockMsg<localizes>... |
message params need a <localizes> helper — you cannot pass a raw string to Show. |
SetMaxTriggerCount(1) |
Clifftop fires exactly once per reset cycle. |
SetResetDelay(2.0) |
After firing, the device waits 2 s before it can fire again. |
HUD.SetDisplayTime(4.0) |
Default display time for messages shown via Show() with no explicit DisplayTime. |
TriggeredEvent.Subscribe(OnDockTriggered) |
Registers the method as a callback. The method signature must match (?agent) : void. |
if (A := Agent?): |
Unwraps the optional. Everything indented under this only runs when a real player triggered the device. |
HUD.Show(A, DockMsg(...)) |
Targeted show — only that player's screen updates. |
HUD.Show(CoveMsg(...)) |
Global show — every player sees it. |
HUD.Show(A, CliffMsg(...), ?DisplayTime := 6.0) |
Named optional param overrides the device's default display time. |
ClifftopTrigger.Enable() |
Re-arms the trigger so the next player can also get their one-shot moment. |
Common patterns
Pattern 1 — Checking trigger budget before firing (GetTriggerCountRemaining)
The lagoon's cannon trigger should only fire while ammo remains. Use GetTriggerCountRemaining to gate a HUD warning.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
CannonWarning<localizes>(S : string) : message = "{S}"
cannon_ammo_checker := class(creative_device):
@editable
CannonTrigger : trigger_device = trigger_device{}
@editable
HUD : hud_message_device = hud_message_device{}
OnBegin<override>()<suspends> : void =
# Give the cannon 5 shots total.
CannonTrigger.SetMaxTriggerCount(5)
CannonTrigger.TriggeredEvent.Subscribe(OnCannonFired)
OnCannonFired(Agent : ?agent) : void =
# GetTriggerCountRemaining tells us shots left AFTER this one.
Remaining := CannonTrigger.GetTriggerCountRemaining()
if (Remaining = 1):
# INDENTATION: only this message shows when exactly 1 shot remains.
HUD.Show(CannonWarning("⚠️ Last cannonball!"))
if (Remaining = 0):
# INDENTATION: this block is a SEPARATE if — both can't be true at once,
# but they are evaluated independently because they are sibling blocks.
HUD.Show(CannonWarning("💥 Cannon is empty!"))
CannonTrigger.Disable()
Pattern 2 — Transmit delay for a dramatic reveal (SetTransmitDelay + code-triggered Trigger)
The pirate ship's foghorn trigger fires from code after a 3-second transmit delay, then shows a message.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
FogMsg<localizes>(S : string) : message = "{S}"
foghorn_reveal := class(creative_device):
@editable
FoghornTrigger : trigger_device = trigger_device{}
@editable
HUD : hud_message_device = hud_message_device{}
OnBegin<override>()<suspends> : void =
# The trigger will wait 3 s before notifying linked devices.
FoghornTrigger.SetTransmitDelay(3.0)
FoghornTrigger.TriggeredEvent.Subscribe(OnFoghorn)
# Fire the trigger from code (no agent) after setup.
# INDENTATION: this call is in OnBegin's block, so it runs at game start.
FoghornTrigger.Trigger()
OnFoghorn(Agent : ?agent) : void =
# Agent is false/none here because we called Trigger() with no agent.
# INDENTATION: the if runs only when a real player triggered it.
if (A := Agent?):
HUD.Show(A, FogMsg("🚢 A ship emerges from the fog!"))
# This always runs — even for code-triggered events.
HUD.Show(FogMsg("🌫️ The foghorn echoes across the lagoon..."))
Pattern 3 — Dynamic text swap with SetText + Hide
The lagoon's tide warning cycles between two messages. SetText changes what the device will show next; Hide clears the current message before swapping.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
TideMsg<localizes>(S : string) : message = "{S}"
tide_warning_cycle := class(creative_device):
@editable
TideTrigger : trigger_device = trigger_device{}
@editable
HUD : hud_message_device = hud_message_device{}
# Track which message to show next.
var ShowingHighTide : logic = false
OnBegin<override>()<suspends> : void =
HUD.SetDisplayTime(0.0) # Persistent until hidden.
TideTrigger.TriggeredEvent.Subscribe(OnTideToggle)
OnTideToggle(Agent : ?agent) : void =
# Hide the current message before swapping text.
HUD.Hide()
if (ShowingHighTide?):
# INDENTATION: this block runs when ShowingHighTide is true.
HUD.SetText(TideMsg("🌊 Low tide — safe to cross the sandbar!"))
set ShowingHighTide = false
else:
# INDENTATION: this else-block is at the SAME level as the if.
HUD.SetText(TideMsg("🌊 HIGH TIDE — stay off the rocks!"))
set ShowingHighTide = true
# Show the newly set text — runs regardless of which branch was taken.
# INDENTATION: back at method level, outside both if and else.
HUD.Show()
Gotchas
1. TriggeredEvent sends ?agent, not agent
The handler signature is (Agent : ?agent) : void. If you write (Agent : agent) the subscription will fail to compile. Always unwrap:
if (A := Agent?):
HUD.Show(A, MyMsg("Hello!"))
Code-triggered calls (Trigger() with no argument) send false for the agent, so the if body is skipped — which is usually what you want.
2. message is NOT a string
HUD.Show, HUD.SetText, and the Show(Agent, Message, ...) overload all take message, not string. You must declare a <localizes> helper:
MyLabel<localizes>(S : string) : message = "{S}"
# Then:
HUD.SetText(MyLabel("Ahoy!"))
There is no StringToMessage function in Verse.
3. SetMaxTriggerCount clamps to [0, 20]
Passing 21 silently becomes 20. Pass 0 for unlimited. Check the remaining count with GetTriggerCountRemaining() — it returns 0 when the limit is unlimited (not when zero shots remain), so don't use it as a "shots left" counter when the limit is 0.
4. int and float do not auto-convert
SetResetDelay takes a float. Writing SetResetDelay(2) will not compile. Write SetResetDelay(2.0).
5. Indentation is the block delimiter — use exactly 4 spaces
Verse uses 4-space indentation. Mixing tabs and spaces, or using 2 spaces, produces confusing parse errors. The most dangerous mistake is accidentally dedenting a line out of an if block:
# WRONG — the HUD.Show runs unconditionally!
if (A := Agent?):
ClifftopTrigger.Enable()
HUD.Show(A, CliffMsg("Found it!")) # A is not in scope here!
# CORRECT — both lines are inside the if.
if (A := Agent?):
ClifftopTrigger.Enable()
HUD.Show(A, CliffMsg("Found it!"))
6. Disable() vs SetMaxTriggerCount(1)
Disable() prevents the trigger from firing at all (even from code). SetMaxTriggerCount(1) lets it fire once then stop — but Enable() re-arms it. Choose based on whether you want the trigger permanently off or just limited.