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_blockinitialization withDefaultTextandDefaultShadowOffsetSetTextto update the message at runtimeSetTextColorto change the label colorSetShadowColor/SetShadowOpacity/SetShadowOffsetto style the drop shadowAddWidgeton acanvasto 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.