player_ui: Show Custom HUD Widgets to Players
Every player on your island has their own private UI canvas — a per-player overlay where you can push custom widgets only they can see. `GetPlayerUI` hands you that canvas so you can call `AddWidget` to splash a cel-shaded treasure map onto the screen the moment a pirate steps onto a dock trigger, then `RemoveWidget` to pull it away when they leave. Master this pattern and you control every HUD, prompt, and splash screen on your island.
Overview
player_ui is the per-player UI canvas that lives behind every player's screen. It is not a placed device — you obtain it at runtime by calling GetPlayerUI(Player) from the /UnrealEngine.com/Temporary/UI module. Once you have the canvas you can:
AddWidget(Widget)— push anywidget(text blocks, overlays, canvases) onto the player's screen.AddWidget(Widget, Slot)— same, but with aplayer_ui_slotfor z-order and alignment control.RemoveWidget(Widget)— pull a widget back off the screen.SetFocus(Widget)— move input focus to a specific focusable widget.
The classic use-case: a player walks onto a pressure plate on the dock → a cel-shaded location title fades in just for them → they walk off → it disappears. Because the UI is per-player, every crew member on your pirate island can see a different widget at the same time.
API Reference
player_ui
The main interface for adding and removing
widgets to a player's UI.
Full public surface, resolved verbatim from the live Epic digest (UnrealEngine.digest.verse).
player_ui<native><public> := class<final><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):
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. |
Walkthrough
Scenario — Dock Arrival Banner
You have a sun-drenched pirate cove. A trigger_device sits at the end of the wooden dock. When a player steps on it, a cel-shaded "Welcome to Cove Hideout!" banner appears only for that player. A second trigger at the shore's edge removes it when they walk away.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/UI }
# Localisation helper — player_ui requires `message`, not raw strings
DockBannerText<localizes>(S : string) : message = "{S}"
dock_arrival_banner := class(creative_device):
# ── Placed devices (wire these up in UEFN) ──────────────────────────
# Trigger at the end of the dock — fires when a player steps on it
@editable
DockEntryTrigger : trigger_device = trigger_device{}
# Trigger at the shore edge — fires when a player walks away
@editable
DockExitTrigger : trigger_device = trigger_device{}
# ── Internal state ──────────────────────────────────────────────────
# We keep a reference to the widget so we can remove it later.
# In a real project you would use a per-player map; here we keep
# a single slot for clarity.
var BannerWidget : ?widget = false
# ── Lifecycle ───────────────────────────────────────────────────────
OnBegin<override>()<suspends> : void =
# Subscribe to both triggers
DockEntryTrigger.TriggeredEvent.Subscribe(OnPlayerArrivedAtDock)
DockExitTrigger.TriggeredEvent.Subscribe(OnPlayerLeftDock)
# Limit the exit trigger to fire once per entry so it stays in sync
DockExitTrigger.SetMaxTriggerCount(1)
DockExitTrigger.SetResetDelay(0.5)
# ── Event handlers ──────────────────────────────────────────────────
# Called when TriggeredEvent fires — the payload is ?agent
OnPlayerArrivedAtDock(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
if (P := player[A]):
ShowDockBanner(P)
OnPlayerLeftDock(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
if (P := player[A]):
HideDockBanner(P)
# ── UI helpers ──────────────────────────────────────────────────────
ShowDockBanner(P : player) : void =
# GetPlayerUI<decides> — must be inside an `if` to handle failure
if (UI := GetPlayerUI[P]):
# Build a simple text widget for the cel-shaded banner
Banner := text_block:
DefaultText := DockBannerText("⚓ Welcome to Cove Hideout!")
# AddWidget pushes it onto this player's private canvas
UI.AddWidget(Banner)
# Store so we can remove it later
set BannerWidget = option{Banner}
HideDockBanner(P : player) : void =
if (UI := GetPlayerUI[P]):
if (W := BannerWidget?):
# RemoveWidget pulls it back off the screen
UI.RemoveWidget(W)
set BannerWidget = false
Line-by-line explanation
| Lines | What's happening |
|---|---|
DockEntryTrigger, DockExitTrigger |
Two @editable trigger devices wired in UEFN. The entry trigger fires when a player steps onto the dock; the exit trigger fires when they walk back to shore. |
OnBegin |
Subscribes both triggers and configures the exit trigger to reset after 0.5 s so it can fire again next time. SetMaxTriggerCount(1) + SetResetDelay are real trigger_device methods. |
OnPlayerArrivedAtDock(MaybeAgent : ?agent) |
TriggeredEvent sends ?agent — always unwrap with if (A := MaybeAgent?) before use. Then cast to player with player[A]. |
GetPlayerUI[P] |
Failable (<decides>) — must live inside an if. Returns the player_ui canvas for that specific player. |
UI.AddWidget(Banner) |
Pushes the text block onto only this player's screen. Other players see nothing. |
UI.RemoveWidget(W) |
Cleanly removes the widget when the player walks away. |
Common patterns
Pattern 1 — One-shot splash with auto-dismiss using SetMaxTriggerCount
A clifftop trigger shows a "Treasure Found!" splash to the triggering player, then auto-removes it after a delay. The trigger is capped at one fire per reset cycle.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
TreasureFoundText<localizes>(S : string) : message = "{S}"
treasure_splash_device := class(creative_device):
@editable
CliffTopTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
# Fire once, then reset after 10 seconds
CliffTopTrigger.SetMaxTriggerCount(1)
CliffTopTrigger.SetResetDelay(10.0)
CliffTopTrigger.TriggeredEvent.Subscribe(OnCliffReached)
OnCliffReached(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
if (P := player[A]):
if (UI := GetPlayerUI[P]):
Splash := text_block:
DefaultText := TreasureFoundText("💰 Treasure Found!")
UI.AddWidget(Splash)
# Auto-remove after 3 seconds
spawn { AutoRemove(UI, Splash) }
AutoRemove(UI : player_ui, W : widget)<suspends> : void =
Sleep(3.0)
UI.RemoveWidget(W)
Pattern 2 — Re-enable a trigger and check remaining count
A lagoon buoy trigger is disabled at start. A separate shore trigger enables it and logs how many fires remain, demonstrating Enable, Disable, GetTriggerCountRemaining, and GetMaxTriggerCount.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
BuoyAlertText<localizes>(S : string) : message = "{S}"
lagoon_buoy_device := class(creative_device):
@editable
BuoyTrigger : trigger_device = trigger_device{}
@editable
ShoreTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
# Buoy starts disabled; shore trigger arms it
BuoyTrigger.Disable()
BuoyTrigger.SetMaxTriggerCount(3) # can fire 3 times total
ShoreTrigger.TriggeredEvent.Subscribe(OnShoreReached)
BuoyTrigger.TriggeredEvent.Subscribe(OnBuoyTriggered)
OnShoreReached(MaybeAgent : ?agent) : void =
BuoyTrigger.Enable() # arm the buoy
OnBuoyTriggered(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
if (P := player[A]):
Remaining := BuoyTrigger.GetTriggerCountRemaining()
Max := BuoyTrigger.GetMaxTriggerCount()
if (UI := GetPlayerUI[P]):
Alert := text_block:
DefaultText := BuoyAlertText("🔔 Buoy hit! Fires left: {Remaining}/{Max}")
UI.AddWidget(Alert)
spawn { AutoRemove(UI, Alert) }
AutoRemove(UI : player_ui, W : widget)<suspends> : void =
Sleep(2.0)
UI.RemoveWidget(W)
Pattern 3 — SetFocus on an interactive widget
A pirate ship trigger opens a cel-shaded choice overlay and focuses the first button so gamepad players can interact immediately.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
ChoicePromptText<localizes>(S : string) : message = "{S}"
ship_choice_device := class(creative_device):
@editable
ShipBoardingTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
ShipBoardingTrigger.TriggeredEvent.Subscribe(OnBoarded)
OnBoarded(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
if (P := player[A]):
if (UI := GetPlayerUI[P]):
# Build a focusable button widget
ChoiceBtn := button_loud:
DefaultText := ChoicePromptText("⚔ Board the ship?")
UI.AddWidget(ChoiceBtn)
# Move gamepad / keyboard focus to this button immediately
UI.SetFocus(ChoiceBtn)
spawn { AutoRemove(UI, ChoiceBtn) }
AutoRemove(UI : player_ui, W : widget)<suspends> : void =
Sleep(5.0)
UI.RemoveWidget(W)
Gotchas
1. GetPlayerUI is failable — always wrap in if
GetPlayerUI is marked <decides>, meaning it can fail (e.g. the player has already left the session). Never call it bare:
# ❌ WRONG — compile error, <decides> not handled
UI := GetPlayerUI(P)
# ✅ CORRECT
if (UI := GetPlayerUI[P]):
UI.AddWidget(MyWidget)
2. TriggeredEvent sends ?agent, not agent
The event payload is optional — the device can fire without a player (e.g. triggered from code). Always unwrap:
# ❌ Treating ?agent as agent crashes at compile time
OnFired(A : agent) : void = ...
# ✅ Correct handler signature + unwrap
OnFired(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?): ...
3. message ≠ string — use a <localizes> helper
text_block.DefaultText and similar widget fields require a message value, not a plain string. There is no StringToMessage function. Declare a thin localisation wrapper:
MyLabel<localizes>(S : string) : message = "{S}"
# Then use:
text_block{ DefaultText := MyLabel("Cove Hideout") }
4. player_ui is per-player — not global
Widgets you add via AddWidget are invisible to everyone else. If you want all players to see the same widget you must call AddWidget inside a loop over every player in the playspace.
5. Remove widgets you no longer need
Widgets are not automatically cleaned up when a player respawns or the round resets. Always call RemoveWidget (or track and remove on round-end) to avoid stale overlays piling up on the screen.
6. SetMaxTriggerCount clamps to [0, 20]
Passing a value outside this range is silently clamped. Use 0 to mean "unlimited". Check GetTriggerCountRemaining() returns 0 when unlimited — don't use it as a countdown in that case.
Guides & scripts that use player_ui
- Build Live Player Stat UI in VerseShow a real-time stat panel on every player's screen by binding Verse state to UI widgets. You'll build a widget hierarchy at runtime, add it through the fallible GetPlayerUI[] handle, and push live updates from a suspen…
- Custom Vehicle Physics: Suspension & Grip in VerseUnlock precise vehicle tuning by calculating spring compression and tire grip directly in Verse. This guide wires a real per-frame physics loop on a creative_prop, applies simulated spring and damping forces with ApplyFo…
- Build Live Score Dashboards with Verse Player UILearn to bind gameplay metrics to on-screen widgets that update in real time for every player. This guide subscribes to a trigger event, computes a metric, and pushes a fresh text widget to each player's UI using the rea…
- Spawning Dynamic Text Widgets on the Player UI with VerseBring your UEFN island to life by adding live text widgets directly to a player's UI at runtime. In this guide you'll subscribe to a button interaction, safely resolve the player's UI layer, add a canvas-based text displ…
- Round-Persistent Survival Timers in VerseSurvival modes demand progress that survives round resets and server reloads. By pairing Verse's `<persistable>` classes with a module-scoped `weak_map`, you can track each player's survival time and mirror it on their s…
- Verse UI: Programmatic Widget Population with GetPlayerUILearn how to build a self-populating UI list in Verse by looping over a data array and injecting a text widget per entry. Using the real `GetPlayerUI[]` and `AddWidget` APIs, you can generate inventory rows, shop slots, …
Build your own lesson with player_ui
Generate a personalized, compile-checked lesson grounded in this exact reference.
Build a lesson →