Overview
The popup_dialog_device is your go-to device when you want to talk to a player and hear back. It draws a HUD panel — a Title, a Description, and a row of buttons — and fires events when the player sees it, dismisses it, lets it time out, or pushes a specific button.
Reach for it when you want to:
- Show a tutorial or briefing card at the start of a round.
- Ask a player a yes/no question ("Open the vault?") and branch on their answer.
- Build a tiny menu (class select, shop, difficulty) where each button means something.
- Display live stats by rewriting button text every few seconds.
The key superpower over a plain message device is RespondingButtonEvent, which tells you the agent who clicked and the int index of the button — so a single popup can drive completely different game logic per button.
API Reference
popup_dialog_device
Used to create HUD text boxes that give players information, and allows responses to be customized to player choices.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
popup_dialog_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
RespondingButtonEvent |
RespondingButtonEvent<public>:listenable(tuple(agent, int)) |
Signaled when Button <Index> on this device is pushed by an agent. Sends the agent that pushed the button. Sends the int index of the button that was clicked. |
ShownEvent |
ShownEvent<public>:listenable(agent) |
Signaled when this device is shown to an agent. Sends the agent looking at the popup. |
DismissedEvent |
DismissedEvent<public>:listenable(agent) |
Signaled when this device is dismissed by an agent. Sends the agent who dismissed the popup. |
TimeOutEvent |
TimeOutEvent<public>:listenable(agent) |
Signaled when this device times out while an agent is looking at it. Sends the agent who was looking at the popup. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
Show |
Show<public>(Agent:agent):void |
Shows the popup to Agent. |
Show |
Show<public>():void |
Shows the popup to all agents in the experience. |
Hide |
Hide<public>(Agent:agent):void |
Hides the popup from Agent. |
Hide |
Hide<public>():void |
Hides the popup from all agents in the experience. |
SetButtonCount |
SetButtonCount<public>(Count:int):void |
Sets the number of buttons this popup has. Button Count is not updated on active Popups. |
GetButtonText |
GetButtonText<public>(Index:int)<transacts>:string |
Returns the Button Text for this popup at a specified index. |
SetButtonText |
SetButtonText<public>(Text:message, Index:int):void |
Sets the Button Text for a button at a specific index on this popup. * Text should be no more than 24 characters. * If Text is empty the button will show OK instead. * Button 1 uses Index 0. |
SetDescriptionText |
SetDescriptionText<public>(Text:message):void |
Sets the Description text for this popup. Text should be no more than 350 characters. |
GetDescriptionText |
GetDescriptionText<public>()<transacts>:string |
Returns the Description text for this popup. |
SetTitleText |
SetTitleText<public>(Text:message):void |
Sets the Title text for this popup. Text should be no more than 32 characters. |
GetTitleText |
GetTitleText<public>()<transacts>:string |
Returns the Title text for this popup. |
Walkthrough
Let's build a vault entrance. When a player steps on a trigger, a popup asks "Open the Vault?" with two buttons: Open it and Walk away. If they pick Open it, we unlock the vault door; if they walk away (or dismiss the popup), nothing happens.
We need three placed devices wired into our class: the popup, a trigger, and a prop-manipulator-style door we'll just enable/disable. For the door we'll use a barrier_device so the example stays self-contained.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
vault_dialog := class(creative_device):
@editable
VaultPopup : popup_dialog_device = popup_dialog_device{}
@editable
EntryTrigger : trigger_device = trigger_device{}
@editable
VaultDoor : barrier_device = barrier_device{}
# Localized message helper — message params can't take raw strings.
DialogText<localizes>(S : string) : message = "{S}"
OnBegin<override>()<suspends> : void =
# Configure the popup before anyone sees it.
VaultPopup.SetButtonCount(2)
VaultPopup.SetTitleText(DialogText("Sealed Vault"))
VaultPopup.SetDescriptionText(DialogText("You found the vault. Open it?"))
VaultPopup.SetButtonText(DialogText("Open it"), 0)
VaultPopup.SetButtonText(DialogText("Walk away"), 1)
# The door starts closed (enabled = blocking).
VaultDoor.Enable()
# When a player steps on the trigger, show the popup to them.
EntryTrigger.TriggeredEvent.Subscribe(OnEntered)
# React to whichever button gets pushed.
VaultPopup.RespondingButtonEvent.Subscribe(OnButtonPushed)
# Optional: if they dismiss it, log nothing happened.
VaultPopup.DismissedEvent.Subscribe(OnDismissed)
OnEntered(MaybeAgent : ?agent) : void =
if (Player := MaybeAgent?):
VaultPopup.Show(Player)
OnButtonPushed(Result : tuple(agent, int)) : void =
Player := Result(0)
Index := Result(1)
if (Index = 0):
# "Open it" — drop the barrier so they can walk in.
VaultDoor.Disable()
VaultPopup.Hide(Player)
else:
# "Walk away" — just close the popup.
VaultPopup.Hide(Player)
OnDismissed(Agent : agent) : void =
# Player closed the popup without choosing; nothing to do.
VaultPopup.Hide(Agent)
Line by line:
- The three
@editablefields let you drop in your placed devices in the UEFN details panel. Without these fields, callingVaultPopup.Show(...)would fail with Unknown identifier — you can only call a device you hold a reference to. DialogText<localizes>(S:string):messageis the bridge from a plain string to themessagetype thatSetTitleText/SetDescriptionText/SetButtonTextrequire. There is noStringToMessage.- In
OnBeginwe configure the popup before showing it:SetButtonCount(2)must be set while the popup is inactive (it doesn't update on already-shown popups). Button 1 isIndex 0, Button 2 isIndex 1. EntryTrigger.TriggeredEventhands us a?agent. We unwrap it withif (Player := MaybeAgent?)and only then callVaultPopup.Show(Player)to show the popup to that player.RespondingButtonEventis alistenable(tuple(agent, int)), so the handler receives onetuplevalue.Result(0)is the clicking agent;Result(1)is the button index. We branch on the index to open the vault or just close.DismissedEventgives us a bareagent(already unwrapped) so we hide the popup for them.
Common patterns
Reading current text with the getters
The getters (GetTitleText, GetDescriptionText, GetButtonText) are <transacts>, so they must be called from a context that allows the transacts effect — here inside a normal method works because reads are pure. Useful for swapping text only when it changed.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
briefing_board := class(creative_device):
@editable
Popup : popup_dialog_device = popup_dialog_device{}
@editable
ShowButton : button_device = button_device{}
Loc<localizes>(S : string) : message = "{S}"
OnBegin<override>()<suspends> : void =
Popup.SetButtonCount(1)
Popup.SetTitleText(Loc("Mission Briefing"))
Popup.SetDescriptionText(Loc("Reach the rooftop before the timer ends."))
Popup.SetButtonText(Loc("Got it"), 0)
ShowButton.InteractedWithEvent.Subscribe(OnInteract)
OnInteract(Agent : agent) : void =
# Read the current title back out, then show to this player.
CurrentTitle := Popup.GetTitleText()
if (CurrentTitle = "Mission Briefing"):
Popup.SetTitleText(Loc("Updated Briefing"))
Popup.Show(Agent)
Showing to everyone and handling a timeout
The no-argument Show() broadcasts to every player. TimeOutEvent fires if a player lets the popup expire without answering — handy for default outcomes.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
round_announce := class(creative_device):
@editable
Announcement : popup_dialog_device = popup_dialog_device{}
Loc<localizes>(S : string) : message = "{S}"
OnBegin<override>()<suspends> : void =
Announcement.Enable()
Announcement.SetButtonCount(1)
Announcement.SetTitleText(Loc("Round Start"))
Announcement.SetDescriptionText(Loc("Storm closes in 60 seconds!"))
Announcement.SetButtonText(Loc("Ready"), 0)
Announcement.ShownEvent.Subscribe(OnShown)
Announcement.TimeOutEvent.Subscribe(OnTimedOut)
# Broadcast to all players at once.
Announcement.Show()
OnShown(Agent : agent) : void =
# Could grant a buff, start a per-player timer, etc.
Announcement.Hide(Agent)
OnTimedOut(Agent : agent) : void =
# Player ignored the prompt — treat as auto-ready.
Announcement.Hide(Agent)
Live stat panel using SetButtonText per slot
Rewriting each button's text turns the popup into a stat readout — exactly the Speedway template pattern. Buttons here act as labelled rows.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
stats_panel := class(creative_device):
@editable
StatsPopup : popup_dialog_device = popup_dialog_device{}
@editable
OpenButton : button_device = button_device{}
Loc<localizes>(S : string) : message = "{S}"
OnBegin<override>()<suspends> : void =
StatsPopup.SetButtonCount(3)
StatsPopup.SetTitleText(Loc("Your Stats"))
OpenButton.InteractedWithEvent.Subscribe(OnOpen)
OnOpen(Agent : agent) : void =
# In a real game these numbers come from stored player stats.
StatsPopup.SetButtonText(Loc("Eliminations: 7"), 0)
StatsPopup.SetButtonText(Loc("Wins: 2"), 1)
StatsPopup.SetButtonText(Loc("Best Time: 41s"), 2)
StatsPopup.Show(Agent)
Gotchas
message, notstring. Every text setter (SetTitleText,SetDescriptionText,SetButtonText) takes amessage. Pass strings through a<localizes>helper likeLoc<localizes>(S:string):message = "{S}". There is noStringToMessagefunction.SetButtonCountwon't update an active popup. Set the count inOnBegin(or beforeShow). Changing it while the popup is on screen does nothing for that showing.- Button indices are 0-based. "Button 1" in the details panel is
Index 0.RespondingButtonEventreports that same 0-based index inResult(1). - Unwrap the tuple correctly.
RespondingButtonEventislistenable(tuple(agent, int)). The handler takes a singletuple(agent, int)parameter; pull the agent withResult(0)and index withResult(1). It is NOT a?agent. - Trigger gives
?agent, popup events giveagentortuple. Atrigger_device'sTriggeredEventhands you?agent— you mustif (P := MaybeAgent?). ButShownEvent/DismissedEvent/TimeOutEventalready hand you a plainagent, so no unwrap is needed. - Text length limits. Title ≤ 32 chars, button text ≤ 24 chars, description ≤ 350 chars. An empty button text falls back to showing OK.
- You must hold a device reference. Declare the device as an
@editablefield and assign it in the UEFN details panel. Calling methods on an un-referenced device is an Unknown identifier compile error.