Reference Verse compiles

text vs string: Showing Live Game Messages on Your Pirate Island HUD

In Verse, `string` and `message` are two different types that look similar but serve very different purposes. A `string` is raw character data you can manipulate in code; a `message` is a *localized*, player-facing value that the HUD and UI systems actually accept. Confusing the two is one of the most common beginner compile errors — this article shows you exactly how they relate, and puts that knowledge to work on a cel-shaded pirate island where a trigger on the dock fires a sequence of HUD me

Updated Examples verified on the live UEFN compiler

Overview

Every time you want to show text to a player in UEFN — a countdown, a score, a story beat — you will touch both types:

Type What it is Where it lives Can be shown on HUD?
string Raw UTF-8 character data. Supports concatenation, comparison, ToString() conversion. Your Verse logic ❌ Not directly
message A localized text value. Produced by a <localizes> function. Player-facing UI / HUD devices ✅ Yes

Why two types?

Fortnite is a global game. The engine needs to know which strings are player-facing (and therefore candidates for translation) versus which are internal (debug labels, key lookups, log text). The message type is that contract. You cannot pass a raw string to hud_message_device.Show() or SetText() — the compiler rejects it. You must wrap it in a <localizes> function first.

The bridge: a <localizes> function

# Declare once at module scope (or class scope):
MyText<localizes>(S : string) : message = "{S}"

Now MyText("Ahoy, sailor!") produces a message you can hand to any HUD API. The {S} inside the string literal is Verse string interpolation — it embeds the value of S into the localized string at runtime.

When to reach for hud_message_device

Use it whenever you need to:

  • Flash a contextual hint ("Step on the gangplank to board!")
  • Show a dynamic value (remaining treasure chests, elapsed time)
  • Sequence story beats without building a full UI widget

API Reference

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.

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):

Walkthrough

Scenario: A cel-shaded pirate island. Three trigger pads are hidden along the dock, the shore, and the clifftop. When a player steps on each one, a unique HUD message appears. After all three are found, the HUD announces "Treasure map complete!" and clears after five seconds. A fourth trigger (a reset pad back at the ship) clears all messages and resets the sequence.

Setup in UEFN editor

  1. Place three trigger_device props at the dock, shore, and clifftop.
  2. Place one trigger_device at the pirate ship (reset).
  3. Place one hud_message_device anywhere on the island.
  4. Create a new Verse device, wire the four triggers and the HUD device as @editable fields.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }

# ─────────────────────────────────────────────────────────────
# Localizes bridge — converts a runtime string into a message
# ─────────────────────────────────────────────────────────────
PirateMsg<localizes>(S : string) : message = "{S}"

# ─────────────────────────────────────────────────────────────
# Main device
# ─────────────────────────────────────────────────────────────
pirate_island_hud_device := class(creative_device):

    # ── Editable fields (wire these in the UEFN editor) ──────
    @editable
    DockTrigger : trigger_device = trigger_device{}

    @editable
    ShoreTrigger : trigger_device = trigger_device{}

    @editable
    ClifftopTrigger : trigger_device = trigger_device{}

    @editable
    ResetTrigger : trigger_device = trigger_device{}

    @editable
    HUD : hud_message_device = hud_message_device{}

    # ── Internal state ────────────────────────────────────────
    var FoundCount : int = 0

    # ── Lifecycle ─────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # Each trigger may only fire once per round
        DockTrigger.SetMaxTriggerCount(1)
        ShoreTrigger.SetMaxTriggerCount(1)
        ClifftopTrigger.SetMaxTriggerCount(1)

        # Display time: 4 seconds for clue messages
        HUD.SetDisplayTime(4.0)

        # Subscribe to each trigger
        DockTrigger.TriggeredEvent.Subscribe(OnDockTriggered)
        ShoreTrigger.TriggeredEvent.Subscribe(OnShoreTriggered)
        ClifftopTrigger.TriggeredEvent.Subscribe(OnClifftopTriggered)
        ResetTrigger.TriggeredEvent.Subscribe(OnResetTriggered)

    # ── Handlers ──────────────────────────────────────────────

    # TriggeredEvent sends ?agent — unwrap before using
    OnDockTriggered(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            set FoundCount += 1
            # Show a custom message to THIS specific agent
            HUD.Show(A, PirateMsg("Dock: X marks the first spot! ({FoundCount}/3)"))
            CheckComplete(A)

    OnShoreTriggered(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            set FoundCount += 1
            HUD.Show(A, PirateMsg("Shore: The tide hides the second clue! ({FoundCount}/3)"))
            CheckComplete(A)

    OnClifftopTriggered(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            set FoundCount += 1
            HUD.Show(A, PirateMsg("Clifftop: The crow's nest reveals all! ({FoundCount}/3)"))
            CheckComplete(A)

    OnResetTriggered(MaybeAgent : ?agent) : void =
        # ClearAllMessages wipes the queue for every player
        HUD.ClearAllMessages()
        set FoundCount = 0
        # Re-enable the three clue triggers for a new round
        DockTrigger.Enable()
        ShoreTrigger.Enable()
        ClifftopTrigger.Enable()

    CheckComplete(A : agent) : void =
        if (FoundCount >= 3):
            # Persistent final message (DisplayTime 0.0 = forever until cleared)
            HUD.Show(A, PirateMsg("Treasure map complete! Return to the ship!"), ?DisplayTime := 0.0)

Line-by-line highlights

Line(s) What's happening
PirateMsg<localizes>(S:string):message The bridge function. {S} is string interpolation — the runtime value of S is embedded in the localized string.
HUD.SetDisplayTime(4.0) Sets the default on-screen duration for all subsequent Show() calls that don't override it.
DockTrigger.SetMaxTriggerCount(1) Clamps the trigger to fire exactly once — prevents duplicate messages.
HUD.Show(A, PirateMsg(...)) The two-arg overload: shows a custom message only on agent A's screen.
?DisplayTime := 0.0 Named optional argument — 0.0 means the message stays until explicitly hidden or cleared.
HUD.ClearAllMessages() Wipes every queued message for all players; used on reset.
if (A := MaybeAgent?) Unwraps the ?agent option type. If no agent triggered the device (code-triggered), the block is skipped safely.

Common patterns

Pattern 1 — Dynamic float value in a HUD message (using ToString)

You often want to show a numeric value — elapsed time, distance, score. ToString<public>(Val:float)<reads>:string converts a float to a string, which you then wrap with your localizes function.

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

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

timer_hud_device := class(creative_device):

    @editable
    HUD : hud_message_device = hud_message_device{}

    @editable
    StartTrigger : trigger_device = trigger_device{}

    var ElapsedSeconds : float = 0.0

    OnBegin<override>()<suspends> : void =
        StartTrigger.TriggeredEvent.Subscribe(OnStartTriggered)

    OnStartTriggered(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            set ElapsedSeconds += 1.0
            # ToString converts float -> string; TimerMsg wraps it as message
            TimeStr : string = ToString(ElapsedSeconds)
            HUD.Show(A, TimerMsg("Time at sea: {TimeStr}s"))

Pattern 2 — Broadcast to ALL players + query display time

Sometimes you want every player to see the same announcement (a wave starting, a ship sinking). Use the no-arg Show(Message) overload and verify the current display time with GetDisplayTime().

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

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

broadcast_hud_device := class(creative_device):

    @editable
    HUD : hud_message_device = hud_message_device{}

    @editable
    WaveTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        WaveTrigger.TriggeredEvent.Subscribe(OnWaveTriggered)

    OnWaveTriggered(MaybeAgent : ?agent) : void =
        # Check current display time before overriding it
        CurrentTime : float = HUD.GetDisplayTime()
        if (CurrentTime < 3.0):
            HUD.SetDisplayTime(5.0)

        # No-arg Show broadcasts to ALL players on the island
        HUD.Show(Announce("A pirate ship approaches the lagoon!"))

Pattern 3 — Hide a message per-agent + subscribe to HideMessageEvent

Sometimes you need to react after a message disappears — for example, to re-enable a trigger only after the player has read the clue.

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

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

hide_aware_device := class(creative_device):

    @editable
    HUD : hud_message_device = hud_message_device{}

    @editable
    ClueTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        ClueTrigger.TriggeredEvent.Subscribe(OnClueTriggered)
        # React when the HUD message is hidden for any agent
        HUD.HideMessageEvent.Subscribe(OnMessageHidden)

    OnClueTriggered(MaybeAgent : ?agent) : void =
        if (A := MaybeAgent?):
            # Show for 6 seconds, then re-enable the trigger
            HUD.Show(A, ClueMsg("The cove entrance is behind the waterfall."), ?DisplayTime := 6.0)
            # Disable while reading
            ClueTrigger.Disable()

    OnMessageHidden(A : agent) : void =
        # Message gone — player has had time to read it; re-enable
        ClueTrigger.Enable()

Gotchas

1. stringmessage — the compiler will not auto-convert

This fails:

# WRONG — string is not message
HUD.SetText("Ahoy!")          # compile error
HUD.Show(A, "Ahoy!")          # compile error

You must go through a <localizes> function:

Msg<localizes>(S : string) : message = "{S}"
HUD.SetText(Msg("Ahoy!"))     # ✅

2. There is NO StringToMessage built-in

A common search. It does not exist. The <localizes> bridge pattern above is the only way to produce a message from a runtime string.

3. ?agent must be unwrapped before use

TriggeredEvent sends ?agent (an option). Passing it directly to HUD.Show(Agent, ...) fails because Show expects agent, not ?agent:

# WRONG
OnTriggered(MaybeAgent : ?agent) : void =
    HUD.Show(MaybeAgent, Msg("Hi"))   # compile error

# CORRECT
OnTriggered(MaybeAgent : ?agent) : void =
    if (A := MaybeAgent?):
        HUD.Show(A, Msg("Hi"))        # ✅

4. SetText vs Show — they do different things

SetText(Text:message) stores the message on the device but does not display it. You still need to call Show() afterwards. Use SetText when you want to pre-configure the message and trigger display later (e.g., from a linked device in the editor).

5. DisplayTime := 0.0 means persistent, not instant

Passing ?DisplayTime := 0.0 keeps the message on screen until you explicitly call Hide() or ClearAllMessages(). A negative value (or omitting the argument) falls back to the device's configured display time.

6. intfloat is not automatic

If you store a counter as int and want to show it, you cannot pass it directly to ToString(Val:float). Either store it as float, or cast: Verse has no implicit numeric coercion. Use a separate IntMsg<localizes>(I:int):message = "{I}" localizes function to embed an int directly via interpolation instead.

Guides & scripts that use hud_message_device

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

Build your own lesson with hud_message_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 →