Reference Unreal Engine compiles

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.

Updated Examples verified on the live UEFN compiler

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 any widget (text blocks, overlays, canvases) onto the player's screen.
  • AddWidget(Widget, Slot) — same, but with a player_ui_slot for 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. messagestring — 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

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

Build your own lesson with player_ui

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 →