Verse Button Events & Player UI Widgets
Tutorialbeginnercompiles

Verse Button Events & Player UI Widgets

Updated beginnerCode verified

What you'll learn

  • How to subscribe to InteractedWithEvent on a button_device.
  • How to safely cast an agent to a player and get their player_ui with GetPlayerUI[...].
  • How to add and later remove a text_block widget from a player's screen.

How it works

  1. Subscribe: In OnBegin, we call Button.InteractedWithEvent.Subscribe(HandleButtonInteraction). Device events use .Subscribe directly (no parentheses on the event), unlike character events.
  2. Handler receives an agent: The handler is passed the agent who interacted. An agent can be a player OR AI, so we can't use it directly for UI.
  3. Cast + get UI in one failure context: if (InPlayer := player[Agent], PlayerUI := GetPlayerUI[InPlayer]): casts the agent to a player and fetches that player's UI. Both are fallible ([]) calls, so they live inside an if condition.
  4. Track state per widget: We store the current widget in a per-player weak_map option. If a widget already exists we remove it, then add a fresh one showing the updated count.

Why the failure context? player[Agent] and GetPlayerUI[InPlayer] are <decides> calls — the [] brackets are only legal inside an if, for, or other failure context. If the agent is an AI, the cast simply fails and we skip the UI code.

Let's build it

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

# Displays a per-player click counter widget when a Button device is pressed.
button_ui_device := class<concrete>(creative_device):

    @editable
    Button : button_device = button_device{}

    # How many times each player has clicked. weak_map keyed by agent.
    var ClickCounts : [agent]int = map{}

    # The current widget shown to each player, so we can remove it before re-adding.
    var ShownWidgets : [agent]?text_block = map{}

    OnBegin<override>()<suspends> : void =
        # Device events subscribe WITHOUT parentheses on the event itself.
        Button.InteractedWithEvent.Subscribe(HandleButtonInteraction)

    # Runs whenever a player interacts with the Button device.
    HandleButtonInteraction(Agent : agent) : void =
        # Cast the agent to a player AND fetch their UI in one failure context.
        # Both [] calls are fallible; AI agents will simply fail the cast here.
        if (InPlayer := player[Agent], PlayerUI := GetPlayerUI[InPlayer]):
            # Compute the new click count for this player.
            var Count : int = 0
            if (Existing := ClickCounts[Agent]):
                set Count = Existing
            set Count = Count + 1
            if (set ClickCounts[Agent] = Count) {}

            # Remove any widget we previously showed this player.
            if (Old := ShownWidgets[Agent]?):
                PlayerUI.RemoveWidget(Old)

            # Build a fresh text widget with the updated count and show it.
            NewWidget : text_block = text_block{DefaultText := ClickMessage}
            NewWidget.SetText(StringToMessage("Clicked {Count} times!"))
            PlayerUI.AddWidget(NewWidget)
            if (set ShownWidgets[Agent] = option{NewWidget}) {}

            Print("Button pressed! Player count is now {Count}")

    # Default text shown before SetText overrides it.
    ClickMessage<localizes> : message = "Clicked!"

    # Wrap a string into a message so text_block can display it.
    StringToMessage<localizes>(S : string) : message = "{S}"

Try it yourself

  • Add a second button_device and give each its own counter map.
  • After showing the widget, Sleep(3.0) then call PlayerUI.RemoveWidget(NewWidget) to auto-hide it (you'll need to restructure into a spawn{}).
  • Wrap the text_block in a stack_box or overlay for custom positioning.

Recap

  • Device events like InteractedWithEvent use .Subscribe(Handler) directly.
  • Handlers get an agent; cast with player[Agent] inside a failure context before touching UI.
  • GetPlayerUI[InPlayer] returns a player_ui; use AddWidget/RemoveWidget to manage that player's on-screen widgets.
  • Track shown widgets in an option map so you can remove the old one before adding a new one.

Check your understanding

Test yourself with an interactive quiz and track your progress + earn XP — free for members.

Turn this into a guided course

Add Subscribing to Button Device Interaction Events in Verse to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.

Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.

Comments

    Sign in to vote, comment, or suggest an edit.Sign in