Reference Fortnite API compiles

text_block: Glowing HUD Text on a Sunlit Dock

Your pirate island needs a scoreboard, a countdown, a welcome banner — anything that talks to the player. `text_block` is the Verse widget that puts words on screen, and it lets you change color, add a drop shadow, and swap the message at runtime. This article shows you exactly how to build, style, and update a `text_block` so your sunny cove feels alive.

Updated Examples verified on the live UEFN compiler

Overview

A text_block is a UI widget that renders a single piece of text on a player's screen. It lives inside a canvas (which controls position and anchoring), and that canvas is handed to the player's player_ui via AddWidget. Once the widget is on screen you can call SetText, SetTextColor, SetShadowColor, SetShadowOffset, and SetShadowOpacity at any time to react to game events — a wave counter ticking up, a treasure chest opening, a timer running out.

Reach for text_block when you need to:

  • Show a persistent HUD label (score, wave number, objective)
  • Flash a contextual message when a player enters a zone
  • Style text with a colored drop shadow for that cel-shaded look

API Reference

text_block

Text block widget. Displays text to the user.

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

text_block<native><public> := class<final>(text_base):

widget

Base class for all UI elements drawn on the player's screen.

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

widget<native><public> := class<abstract><unique><epic_internal>:

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 — Dock Welcome Banner

A player sails into a sun-drenched cove and steps onto the dock. A glowing yellow banner fades in at the top of their screen reading "Welcome to Cove Station!" The text has a teal drop shadow offset diagonally — perfect for that 2D cel-shaded aesthetic. When the player leaves the dock trigger zone, the text dims to grey to signal they've wandered off.

This device uses:

  • text_block initialization with DefaultText and DefaultShadowOffset
  • SetText to update the message at runtime
  • SetTextColor to change the label color
  • SetShadowColor / SetShadowOpacity / SetShadowOffset to style the drop shadow
  • AddWidget on a canvas to put it on screen
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }

# Localisation helper — converts a plain string into a message value.
DockText<localizes>(S : string) : message = "{S}"

dock_welcome_device := class(creative_device):

    # Wire this to a Trigger Device placed on the dock planks.
    @editable DockTrigger : trigger_device = trigger_device{}

    # Wire this to a second Trigger Device at the edge of the dock.
    @editable LeaveTrigger : trigger_device = trigger_device{}

    # We keep a reference to each player's text block so we can update it later.
    var PlayerLabels : [player]text_block = map{}

    OnBegin<override>()<suspends> : void =
        # Subscribe to both triggers at startup.
        DockTrigger.TriggeredEvent.Subscribe(OnPlayerArrived)
        LeaveTrigger.TriggeredEvent.Subscribe(OnPlayerLeft)

    # Called when a player steps onto the dock.
    OnPlayerArrived(Agent : ?agent) : void =
        if (A := Agent?, P := player[A]):
            ShowDockBanner(P)

    # Called when a player walks off the dock.
    OnPlayerLeft(Agent : ?agent) : void =
        if (A := Agent?, P := player[A]):
            DimDockBanner(P)

    # Builds the text_block, wraps it in a canvas, and adds it to the player's UI.
    ShowDockBanner(P : player) : void =
        if (PlayerUI := GetPlayerUI[P]):
            # --- Build the text_block ---
            # DefaultText sets the initial string (must be a message, not a raw string).
            # DefaultShadowOffset positions the shadow diagonally down-right.
            # DefaultShadowColor / DefaultShadowOpacity set its initial look.
            Label := text_block:
                DefaultText        := DockText("Welcome to Cove Station!")
                DefaultShadowOffset  := option{ vector2{X := 3.0, Y := 3.0} }
                DefaultShadowColor   := MakeColorFromHex("00FFCC")  # teal
                DefaultShadowOpacity := 0.85

            # Paint the text sunny yellow.
            Label.SetTextColor(MakeColorFromHex("FFD700"))

            # --- Wrap in a canvas so we can anchor it to the top-centre ---
            Canvas := canvas:
                Slots := array:
                    canvas_slot:
                        Anchors      := anchors{ Minimum := vector2{X := 0.5, Y := 0.05},
                                                 Maximum := vector2{X := 0.5, Y := 0.05} }
                        Offsets      := margin{Left := 0.0, Top := 0.0,
                                               Right := 0.0, Bottom := 0.0}
                        Alignment    := vector2{X := 0.5, Y := 0.0}
                        SizeToContent := true
                        Widget       := Label

            PlayerUI.AddWidget(Canvas)

            # Store the label so we can update it when the player leaves.
            if (set PlayerLabels[P] = Label) {}

    # Dims the banner when the player wanders off the dock.
    DimDockBanner(P : player) : void =
        if (Label := PlayerLabels[P]):
            # Grey text signals "you've left the zone".
            Label.SetTextColor(MakeColorFromHex("888888"))
            # Swap the message to a contextual hint.
            Label.SetText(DockText("Return to the dock!"))
            # Soften the shadow to match the dimmed palette.
            Label.SetShadowOpacity(0.3)

Line-by-line highlights

Line / call Why it matters
DockText<localizes>(S:string):message Verse requires message values for all text APIs. This helper wraps any string.
DefaultShadowOffset := option{ vector2{X:=3.0,Y:=3.0} } DefaultShadowOffset is ?vector2 — you must wrap it in option{…}.
Label.SetTextColor(MakeColorFromHex("FFD700")) Colors the text at runtime; called after construction.
canvas_slot with SizeToContent := true Lets the block grow/shrink with the text automatically.
if (set PlayerLabels[P] = Label) {} Mutable map assignment in Verse always uses set and must be inside a failable context.
Label.SetText(DockText("Return to the dock!")) Updates the displayed string without rebuilding the widget.
Label.SetShadowOpacity(0.3) Adjusts shadow intensity at runtime — independent of color.

Common patterns

Pattern 1 — Animated wave counter (SetText on a timer)

Update a wave number label every few seconds to keep the HUD fresh.

using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }

WaveText<localizes>(N : int) : message = "Wave {N}"

wave_counter_device := class(creative_device):

    @editable WaveStartTrigger : trigger_device = trigger_device{}

    var CurrentWave : int = 1
    var WaveLabel : ?text_block = false

    OnBegin<override>()<suspends> : void =
        WaveStartTrigger.TriggeredEvent.Subscribe(OnWaveStart)

    OnWaveStart(Agent : ?agent) : void =
        if (A := Agent?, P := player[A]):
            if (PlayerUI := GetPlayerUI[P]):
                Label := text_block:
                    DefaultText := WaveText(CurrentWave)

                Canvas := canvas:
                    Slots := array:
                        canvas_slot:
                            Anchors      := anchors{ Minimum := vector2{X := 0.5, Y := 0.1},
                                                     Maximum := vector2{X := 0.5, Y := 0.1} }
                            Offsets      := margin{Left := 0.0, Top := 0.0,
                                                   Right := 0.0, Bottom := 0.0}
                            Alignment    := vector2{X := 0.5, Y := 0.0}
                            SizeToContent := true
                            Widget       := Label

                PlayerUI.AddWidget(Canvas)
                if (set WaveLabel = option{Label}) {}

                # Tick the wave number every 10 seconds.
                loop:
                    Sleep(10.0)
                    set CurrentWave = CurrentWave + 1
                    if (L := WaveLabel?):
                        L.SetText(WaveText(CurrentWave))

Pattern 2 — Drop-shadow style toggle (SetShadowOffset / SetShadowColor)

Toggle between a "day" look (warm shadow) and a "night" look (cool shadow) to match a day-cycle event.

using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }

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

shadow_style_device := class(creative_device):

    @editable NightTrigger : trigger_device = trigger_device{}
    @editable DayTrigger   : trigger_device = trigger_device{}

    var StyleLabel : ?text_block = false

    OnBegin<override>()<suspends> : void =
        NightTrigger.TriggeredEvent.Subscribe(OnNight)
        DayTrigger.TriggeredEvent.Subscribe(OnDay)

        # Build the label once for the first player who joins.
        Players := GetPlayspace().GetPlayers()
        if (P := Players[0], PlayerUI := GetPlayerUI[P]):
            Label := text_block:
                DefaultText          := TimeLabel("Cove Station")
                DefaultShadowOffset  := option{ vector2{X := 2.0, Y := 2.0} }
                DefaultShadowColor   := MakeColorFromHex("FF8800")  # warm amber
                DefaultShadowOpacity := 0.9

            Label.SetTextColor(MakeColorFromHex("FFFFFF"))

            Canvas := canvas:
                Slots := array:
                    canvas_slot:
                        Anchors      := anchors{ Minimum := vector2{X := 0.5, Y := 0.02},
                                                 Maximum := vector2{X := 0.5, Y := 0.02} }
                        Offsets      := margin{Left := 0.0, Top := 0.0,
                                               Right := 0.0, Bottom := 0.0}
                        Alignment    := vector2{X := 0.5, Y := 0.0}
                        SizeToContent := true
                        Widget       := Label

            PlayerUI.AddWidget(Canvas)
            if (set StyleLabel = option{Label}) {}

    OnNight(Agent : ?agent) : void =
        if (L := StyleLabel?):
            # Cool blue shadow for night.
            L.SetShadowColor(MakeColorFromHex("0044FF"))
            L.SetShadowOffset(option{ vector2{X := 4.0, Y := 4.0} })
            L.SetShadowOpacity(1.0)
            L.SetTextColor(MakeColorFromHex("AACCFF"))

    OnDay(Agent : ?agent) : void =
        if (L := StyleLabel?):
            # Warm amber shadow for day.
            L.SetShadowColor(MakeColorFromHex("FF8800"))
            L.SetShadowOffset(option{ vector2{X := 2.0, Y := 2.0} })
            L.SetShadowOpacity(0.9)
            L.SetTextColor(MakeColorFromHex("FFFFFF"))

Pattern 3 — Read back shadow state (GetShadowOffset / GetShadowOpacity)

Inspect the current shadow values before deciding whether to update them — useful for conditional logic.

using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }

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

shadow_readback_device := class(creative_device):

    @editable CheckTrigger : trigger_device = trigger_device{}

    var TrackedLabel : ?text_block = false

    OnBegin<override>()<suspends> : void =
        CheckTrigger.TriggeredEvent.Subscribe(OnCheck)

        Players := GetPlayspace().GetPlayers()
        if (P := Players[0], PlayerUI := GetPlayerUI[P]):
            Label := text_block:
                DefaultText          := ReadbackLabel("Lagoon Depth: 0m")
                DefaultShadowOffset  := option{ vector2{X := 2.0, Y := 2.0} }
                DefaultShadowOpacity := 0.5

            Canvas := canvas:
                Slots := array:
                    canvas_slot:
                        Anchors      := anchors{ Minimum := vector2{X := 0.1, Y := 0.9},
                                                 Maximum := vector2{X := 0.1, Y := 0.9} }
                        Offsets      := margin{Left := 0.0, Top := 0.0,
                                               Right := 0.0, Bottom := 0.0}
                        Alignment    := vector2{X := 0.0, Y := 1.0}
                        SizeToContent := true
                        Widget       := Label

            PlayerUI.AddWidget(Canvas)
            if (set TrackedLabel = option{Label}) {}

    OnCheck(Agent : ?agent) : void =
        if (L := TrackedLabel?):
            # Read current opacity; only boost it if it's already faint.
            CurrentOpacity := L.GetShadowOpacity()
            if (CurrentOpacity < 0.6):
                L.SetShadowOpacity(0.9)
                L.SetText(ReadbackLabel("Shadow boosted!"))
            # Read current offset; log whether a shadow is active.
            CurrentOffset := L.GetShadowOffset()
            if (Offset := CurrentOffset?):
                L.SetShadowColor(MakeColorFromHex("00FF88"))

Gotchas

1. message vs string — the #1 compile error

Every text API (DefaultText, SetText) takes a message, not a string. You cannot pass a raw string literal. Always declare a <localizes> helper:

# CORRECT
MyMsg<localizes>(S : string) : message = "{S}"
Label.SetText(MyMsg("Hello Cove!"))

# WRONG — does not compile
# Label.SetText("Hello Cove!")

2. DefaultShadowOffset is ?vector2 — wrap it in option{…}

# CORRECT
DefaultShadowOffset := option{ vector2{X := 3.0, Y := 3.0} }

# WRONG — type mismatch
# DefaultShadowOffset := vector2{X := 3.0, Y := 3.0}

Same rule applies to SetShadowOffset at runtime — pass option{…} or false to clear the shadow.

3. DefaultShadow* fields are init-only

DefaultShadowColor, DefaultShadowOffset, and DefaultShadowOpacity only take effect at construction time. To change them after the widget is on screen, call the Set* methods (SetShadowColor, SetShadowOffset, SetShadowOpacity).

4. Mutable map assignment needs set inside a failable context

# CORRECT
if (set PlayerLabels[P] = Label) {}

# WRONG — bare assignment outside failable context won't compile
# PlayerLabels[P] = Label

5. GetPlayerUI is failable — always use if

GetPlayerUI has the <decides> effect, meaning it can fail (e.g., if the player has no UI). Always call it inside an if expression:

if (PlayerUI := GetPlayerUI[P]):
    PlayerUI.AddWidget(Canvas)

6. text_block has no events

text_block is a display-only widget. It fires no click or interaction events. If you need user input, look at button_loud or button_quiet instead.

7. AddWidget without a player_ui_slot uses defaults

The zero-argument overload of AddWidget(Widget:widget):void places the widget with default slot settings (full-screen, ZOrder 0). Use a canvas with explicit canvas_slot anchors whenever you need precise positioning.

Guides & scripts that use text_block

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

Build your own lesson with text_block

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 →