Booth Buttons: Bridging Device Interactions into Verse

In this lesson you'll learn to

  • βœ“Student can subscribe to button InteractedWithEvent, read the interacting agent, and route it into their own functions.

Student can subscribe to button InteractedWithEvent, read the interacting agent, and route it into their own functions.

πŸ“– Reference & full walkthrough: Booth Buttons: Bridging Device Interactions into Verse

πŸ” Builds on: south-shores: one-responsibility-per-function (clean handler functions)

🧩 Your capstone piece: The interact button at each of the 5 zone-portal booths


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.
πŸ”’

Keep going β€” free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/sug-subscribing-to-button-device-interaction-events-in-verse
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🧠 Center Village (Free) expedition.

β›΅ Take me there