Reference Unreal Engine

button_device: The Dock Lever That Opens the Vault

The `button_device` is the most direct way to let a player *choose* to do something on your island — hold a lever, press a panel, ring a bell. Drop one on a sun-drenched dock, subscribe to `InteractedWithEvent`, and your Verse code runs the moment the hold-bar fills. This article covers every method and event on `button_device` so you can tune the feel of every interaction from a single Verse class.

Updated

Overview

The button_device is a placeable Creative device that shows a hold-to-interact prompt when a player walks up to it. When the player holds the interact key for the configured duration, InteractedWithEvent fires and your Verse code receives the agent who pressed it.

Use button_device when you need:

  • A one-shot trigger (open the vault door once, then disable the button)
  • A limited-use action (only the first three pirates can claim treasure)
  • A contextual prompt whose label changes at runtime ("Pull the anchor chain" vs "Chain already pulled")
  • A timed interaction that rewards patient players (hold for 3 s to defuse the bomb)

The button widget (UI class) is a separate thing — it lives inside a HUD canvas and fires OnClick when clicked in a UI panel. Both are covered in the API Reference and Common Patterns below.

API Reference

button

Button is a container of a single child widget slot and fires the OnClick event when the button is clicked.

Full public surface, resolved verbatim from the live Epic digest (UnrealEngine.digest.verse). Inherited members are merged from widget.

button<native><public> := class<final>(widget):

Events (subscribe a handler to react):

Event Signature Description
OnClick OnClick<public>():listenable(widget_message) Subscribable event that fires when the button is clicked.
HighlightEvent HighlightEvent<public>():listenable(widget_message)
UnhighlightEvent UnhighlightEvent<public>():listenable(widget_message)

button_device

Used to create a button which can trigger other devices when an agent interacts with it.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

button_device<public> := class<concrete><final>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
InteractedWithEvent InteractedWithEvent<public>:listenable(agent) Signaled when an agent successfully interacts with the button for GetInteractionTime seconds. Sends the agent that interacted with the button.

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.
SetInteractionText SetInteractionText<public>(Text:message):void Sets the text that displays when an agent is close to this button and looks at it. Text is limited to 64 characters.
SetInteractionTime SetInteractionTime<public>(Time:float):void Sets the duration of the interaction required to activate this device (in seconds).
GetInteractionTime GetInteractionTime<public>()<transacts>:float Returns the duration of the interaction required to activate this device (in seconds).
SetMaxTriggerCount SetMaxTriggerCount<public>(MaxCount:int):void Sets the maximum amount of times this button can be interacted with before it will be disabled. * MaxCount must be between 0 and 10000. * 0 indicates no limit on trigger count.
GetMaxTriggerCount GetMaxTriggerCount<public>()<transacts>:int Returns the maximum amount of times this button can be interacted with before it will be disabled. * GetTriggerMaxCount will be between 0 and 10000. * 0 indicates no limit on trigger count.
GetTriggerCountRemaining GetTriggerCountRemaining<public>()<transacts>:int Returns the number of times that this button can still be interacted with before it will be disabled. Will return 0 if GetMaxTriggerCount is unlimited.

Walkthrough

Scenario — The Sunlit Dock Treasure Vault

Your 2D cel-shaded island has a wooden dock stretching into a turquoise lagoon. At the end of the dock sits a brass lever (button_device). When a player holds the lever for 2 seconds, the vault door swings open — but only once per game session. After it fires, the lever dims and the interaction text updates to "Already opened" so latecomers know they missed it.

using { /Fortnite.com/Devices }
using { /Fortnite.com/Devices/Patchwork }
using { /Verse.org/Simulation }

# dock_vault_manager — place this Verse device on your island,
# then assign the lever button and the vault door in the Details panel.
dock_vault_manager := class(creative_device):

    # The brass lever at the end of the dock.
    @editable
    DockLever : button_device = button_device{}

    # A Patchwork door device that represents the vault entrance.
    @editable
    VaultDoor : door_device = door_device{}

    # Localised helper — button_device.SetInteractionText requires a `message`.
    AlreadyOpenedText<localizes>(S : string) : message = "{S}"
    PullLeverText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        # 1. Tell players exactly what to do.
        DockLever.SetInteractionText(PullLeverText("Pull the anchor lever"))

        # 2. Require a 2-second hold — builds tension on the dock.
        DockLever.SetInteractionTime(2.0)

        # 3. Only one player can ever open this vault per session.
        DockLever.SetMaxTriggerCount(1)

        # 4. Log the configured values so designers can verify in output.
        var HoldTime : float = DockLever.GetInteractionTime()
        var MaxUses  : int   = DockLever.GetMaxTriggerCount()
        var UsesLeft : int   = DockLever.GetTriggerCountRemaining()
        Print("Lever hold time: {HoldTime}s | Max uses: {MaxUses} | Remaining: {UsesLeft}")

        # 5. Subscribe — OnVaultOpened runs when a player completes the hold.
        DockLever.InteractedWithEvent.Subscribe(OnVaultOpened)

    # Called with the agent who pulled the lever.
    OnVaultOpened(Pirate : agent) : void =
        # Open the vault door for this specific pirate.
        VaultDoor.Open(Pirate)

        # Update the prompt so anyone who arrives late sees why it's open.
        DockLever.SetInteractionText(AlreadyOpenedText("Vault already open"))

        # MaxTriggerCount(1) already disables the button automatically,
        # but we call Disable() explicitly for clarity and instant feedback.
        DockLever.Disable()

Line-by-line explanation

Lines What's happening
@editable DockLever Exposes the button to the UEFN Details panel so you can drag the in-level device onto this field.
<localizes> helpers SetInteractionText takes a message, not a raw string. These one-liner functions wrap any string literal into a valid message.
SetInteractionText(...) Changes the hold-prompt label at runtime — great for state-dependent context.
SetInteractionTime(2.0) Players must hold for exactly 2 seconds; default is usually instant.
SetMaxTriggerCount(1) After one successful interaction the device auto-disables. 0 means unlimited.
GetInteractionTime() / GetMaxTriggerCount() / GetTriggerCountRemaining() Read back the current config — useful for designer validation or adaptive difficulty.
InteractedWithEvent.Subscribe(OnVaultOpened) Registers the handler. The event sends an agent (not ?agent), so no unwrap is needed.
VaultDoor.Open(Pirate) Passes the instigating agent to the door — required by the Patchwork door API.
DockLever.Disable() Immediately greys out the lever so no second player can attempt it.

Common patterns

Pattern 1 — Re-enable the lever after a cooldown

Sometimes you want the button to be usable again after a short wait — like a cannon on the pirate ship that can fire every 10 seconds.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

cannon_cooldown_manager := class(creative_device):

    @editable
    CannonButton : button_device = button_device{}

    ReadyText<localizes>(S : string) : message = "{S}"
    CooldownText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        # Unlimited uses — 0 means no cap.
        CannonButton.SetMaxTriggerCount(0)
        CannonButton.SetInteractionText(ReadyText("Fire the cannon!"))
        CannonButton.InteractedWithEvent.Subscribe(OnCannonFired)

    OnCannonFired(Crew : agent) : void =
        # Disable immediately so the cooldown is enforced.
        CannonButton.Disable()
        CannonButton.SetInteractionText(CooldownText("Reloading..."))

        # Wait 10 seconds, then re-enable.
        spawn { ReloadCannon() }

    ReloadCannon()<suspends> : void =
        Sleep(10.0)
        CannonButton.Enable()
        CannonButton.SetInteractionText(ReadyText("Fire the cannon!"))

Pattern 2 — Dynamic interaction time based on player count

On a cove island you want the anchor to rise faster when more pirates help. Query GetTriggerCountRemaining to adjust difficulty.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

anchor_lift_manager := class(creative_device):

    @editable
    AnchorButton : button_device = button_device{}

    # Allow up to 5 lifts total (one per crew member).
    MaxCrewLifts : int = 5

    LiftText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        AnchorButton.SetMaxTriggerCount(MaxCrewLifts)
        AnchorButton.SetInteractionTime(3.0)
        AnchorButton.SetInteractionText(LiftText("Heave the anchor!"))
        AnchorButton.InteractedWithEvent.Subscribe(OnHeave)

    OnHeave(Sailor : agent) : void =
        var Remaining : int = AnchorButton.GetTriggerCountRemaining()
        # Reduce hold time as more crew members contribute.
        # Each heave shaves 0.4 s off the required hold.
        var NewTime : float = 3.0 - (float(MaxCrewLifts - Remaining) * 0.4)
        if (NewTime > 0.5):
            AnchorButton.SetInteractionTime(NewTime)

Pattern 3 — Highlight / Unhighlight with the UI button widget

This pattern uses the UI widget button (not button_device) to build a HUD panel on the clifftop — for example, a treasure map overlay. It subscribes to OnClick, HighlightEvent, and UnhighlightEvent.

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

treasure_map_ui_manager := class(creative_device):

    # A button_device in the level that opens the map UI when interacted with.
    @editable
    MapStand : button_device = button_device{}

    OpenMapText<localizes>(S : string) : message = "{S}"
    MapButtonLabel<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        MapStand.SetInteractionText(OpenMapText("Study the treasure map"))
        MapStand.SetInteractionTime(1.0)
        MapStand.InteractedWithEvent.Subscribe(OnMapOpened)

    OnMapOpened(Explorer : agent) : void =
        if (Player := player[Explorer]):
            if (UI := GetPlayerUI[Player]):
                # Build a simple UI button widget and subscribe to its events.
                MapBtn := button:
                    DefaultText := MapButtonLabel("Close Map")

                # OnClick fires when the player clicks the widget button.
                MapBtn.OnClick().Subscribe(OnMapButtonClicked)

                # HighlightEvent / UnhighlightEvent fire on hover.
                MapBtn.HighlightEvent().Subscribe(OnMapButtonHighlighted)
                MapBtn.UnhighlightEvent().Subscribe(OnMapButtonUnhighlighted)

                UI.AddWidget(MapBtn)

    OnMapButtonClicked(Msg : widget_message) : void =
        # Close the map — in a real island you'd RemoveWidget here.
        Print("Map closed by player.")

    OnMapButtonHighlighted(Msg : widget_message) : void =
        Print("Map button hovered.")

    OnMapButtonUnhighlighted(Msg : widget_message) : void =
        Print("Map button unhovered.")

Gotchas

1. SetInteractionText requires a message, not a string

You cannot pass a raw string literal directly:

# WRONG — compile error
DockLever.SetInteractionText("Pull lever")

# RIGHT — wrap it in a <localizes> function
MyLabel<localizes>(S : string) : message = "{S}"
DockLever.SetInteractionText(MyLabel("Pull lever"))

There is no StringToMessage function in Verse. The <localizes> wrapper is the only correct approach.

2. InteractedWithEvent sends agent, not ?agent

button_device.InteractedWithEvent is typed listenable(agent) — the handler receives a concrete agent, not an optional. You do not need to unwrap it with if (A := Agent?).

3. SetMaxTriggerCount(0) means unlimited, not zero uses

Passing 0 removes the cap entirely. To make a one-shot button, pass 1.

4. GetTriggerCountRemaining() returns 0 when the limit is unlimited

If GetMaxTriggerCount() returns 0 (unlimited), GetTriggerCountRemaining() also returns 0 — not a meaningful remaining count. Check GetMaxTriggerCount() first before using the remaining value as a counter.

5. Disable() / Enable() vs SetMaxTriggerCount

Both can prevent interaction, but they're independent. Disable() immediately prevents any interaction. SetMaxTriggerCount(N) auto-disables after N uses but can be overridden by calling Enable() again. Use both together for the clearest intent.

6. button (UI widget) vs button_device (world device)

These are two completely different types. button_device lives in the 3D world and players walk up to it. button is a UI widget added to a player's HUD canvas via player_ui. Don't mix up their APIs — button_device has no OnClick; button has no InteractedWithEvent.

7. float literals for SetInteractionTime

Verse does not auto-convert int to float. Write 2.0, not 2:

# WRONG
DockLever.SetInteractionTime(2)
# RIGHT
DockLever.SetInteractionTime(2.0)

Guides & scripts that use button

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

Build your own lesson with button

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 →