Reference Devices compiles

button_device: Interactive Buttons That Drive Game Logic

The `button_device` is one of the most versatile tools in UEFN — it lets players physically interact with a prop to trigger anything from opening a vault door to activating a boss encounter. In Verse you can subscribe to its `InteractedWithEvent`, dynamically change its prompt text, tune how long a player must hold it, and cap how many times it can be used before it locks out. This article covers every method and event on `button_device` with real, compilable examples.

Updated Examples verified on the live UEFN compiler

Overview

The button_device represents a physical interactable button placed in your island. When a player walks up to it and holds the interact key for the configured duration, the button fires InteractedWithEvent and sends the agent who pressed it.

When to reach for it:

  • A vault door that only opens after a player presses a button for 3 seconds
  • A one-time-use switch that arms a trap and then permanently disables itself
  • A puzzle where the prompt text changes dynamically to hint at the next step
  • Any situation where you need player-initiated, hold-to-confirm input

Unlike a trigger plate (which fires automatically on contact), the button requires deliberate player action and supports configurable hold time — making it ideal for high-stakes or gated interactions.

API Reference

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: A Vault Door with a Timed Button and a Use Limit

You have a vault door made from a barrier device. Players must hold a button for 2 seconds to open it, but the button only works 3 times total (simulating a limited power supply). After each press, the prompt updates to show how many uses remain. On the final press, the button disables itself permanently.

Place a button_device, a barrier_device, and this Verse device in your island. Wire them up via the @editable fields in the Details panel.

vault_button_device := class(creative_device):

    # Wire this to the Button device placed in your level
    @editable
    VaultButton : button_device = button_device{}

    # Wire this to the Barrier device acting as the vault door
    @editable
    VaultDoor : barrier_device = barrier_device{}

    # Localized helper so we can pass dynamic strings as 'message'
    UpdatedPrompt<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        # Set a 2-second hold time — players must commit to opening the vault
        VaultButton.SetInteractionTime(2.0)

        # Cap the button at 3 total uses
        VaultButton.SetMaxTriggerCount(3)

        # Give players a clear initial prompt
        VaultButton.SetInteractionText(UpdatedPrompt("Open Vault (3 uses left)"))

        # Subscribe: every successful interaction calls OnVaultButtonPressed
        VaultButton.InteractedWithEvent.Subscribe(OnVaultButtonPressed)

    OnVaultButtonPressed(Interactor : agent) : void =
        # Disable the barrier so the vault door opens
        VaultDoor.Disable()

        # Check how many presses remain after this one
        Remaining := VaultButton.GetTriggerCountRemaining()

        if (Remaining > 0):
            # Update the prompt to reflect remaining uses
            VaultButton.SetInteractionText(UpdatedPrompt("Open Vault ({Remaining} uses left)"))
        else:
            # No uses left — disable the button so it can't be interacted with
            VaultButton.Disable()
            VaultButton.SetInteractionText(UpdatedPrompt("Vault Power Depleted"))

Line-by-line breakdown:

Line What it does
@editable VaultButton Declares the button reference so you can assign the placed device in the UEFN Details panel. Without @editable and the class field, the identifier is unknown at compile time.
UpdatedPrompt<localizes> Verse's SetInteractionText takes a message (a localized type), not a raw string. This helper wraps any string into a message.
SetInteractionTime(2.0) Players must hold the button for 2 full seconds — prevents accidental presses near the vault.
SetMaxTriggerCount(3) The button auto-disables after 3 successful interactions. Pass 0 for unlimited.
SetInteractionText(...) Updates the floating prompt players see when they look at the button.
InteractedWithEvent.Subscribe(OnVaultButtonPressed) Registers the handler. The event fires only after the full hold time completes.
GetTriggerCountRemaining() Returns how many presses are left. Returns 0 when GetMaxTriggerCount() is 0 (unlimited).
VaultButton.Disable() Prevents any further interaction once the power is gone.

Common patterns

Pattern 1 — Toggle a device on/off with a single button

A player presses a button to toggle a storm controller on and off. Demonstrates Enable / Disable and the InteractedWithEvent handler pattern.

storm_toggle_device := class(creative_device):

    @editable
    ToggleButton : button_device = button_device{}

    @editable
    StormController : storm_controller_device = storm_controller_device{}

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

    var StormActive : logic = false

    OnBegin<override>()<suspends> : void =
        ToggleButton.SetInteractionText(PromptOn("Activate Storm"))
        ToggleButton.InteractedWithEvent.Subscribe(OnTogglePressed)

    OnTogglePressed(Interactor : agent) : void =
        if (StormActive?):
            StormController.Deactivate()
            set StormActive = false
            ToggleButton.SetInteractionText(PromptOn("Activate Storm"))
        else:
            StormController.Activate()
            set StormActive = true
            ToggleButton.SetInteractionText(PromptOn("Deactivate Storm"))

Pattern 2 — Read interaction time and max count for a HUD display

Before the round starts, query the button's configured values and print them to the log for debugging or to feed into a UI widget. Demonstrates GetInteractionTime and GetMaxTriggerCount.

button_info_logger := class(creative_device):

    @editable
    InfoButton : button_device = button_device{}

    OnBegin<override>()<suspends> : void =
        HoldTime := InfoButton.GetInteractionTime()
        MaxUses  := InfoButton.GetMaxTriggerCount()

        # MaxUses == 0 means unlimited
        if (MaxUses = 0):
            Print("Button hold time: {HoldTime}s | Uses: unlimited")
        else:
            Print("Button hold time: {HoldTime}s | Max uses: {MaxUses}")

        InfoButton.InteractedWithEvent.Subscribe(OnInfoButtonPressed)

    OnInfoButtonPressed(Interactor : agent) : void =
        Remaining := InfoButton.GetTriggerCountRemaining()
        Print("Button pressed. Uses remaining: {Remaining}")

Pattern 3 — Race two buttons: first player to press wins

Two teams compete to press their respective buttons first. Uses race so whichever button fires first wins and the other is immediately disabled. Demonstrates Disable and concurrent event awaiting.

first_press_wins_device := class(creative_device):

    @editable
    RedButton  : button_device = button_device{}

    @editable
    BlueButton : button_device = button_device{}

    @editable
    RedReward  : item_granter_device = item_granter_device{}

    @editable
    BlueReward : item_granter_device = item_granter_device{}

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

    OnBegin<override>()<suspends> : void =
        RedButton.SetInteractionText(WinText("Press for Red Team!"))
        BlueButton.SetInteractionText(WinText("Press for Blue Team!"))

        # Race: whichever branch completes first wins; the other is cancelled
        race:
            block:
                RedWinner := RedButton.InteractedWithEvent.Await()
                BlueButton.Disable()
                RedReward.GrantItem(RedWinner)
                RedButton.SetInteractionText(WinText("Red Team Wins!"))
            block:
                BlueWinner := BlueButton.InteractedWithEvent.Await()
                RedButton.Disable()
                BlueReward.GrantItem(BlueWinner)
                BlueButton.SetInteractionText(WinText("Blue Team Wins!"))

Gotchas

1. @editable is mandatory — you cannot construct a live device in code

You must declare button_device as an @editable field inside your class(creative_device). Writing MyButton := button_device{} inside OnBegin gives you a disconnected default object — it is not the device placed in your level and none of its events will fire.

# WRONG — this is a blank object, not your placed device
OnBegin<override>()<suspends> : void =
    MyButton := button_device{}
    MyButton.InteractedWithEvent.Subscribe(OnPressed)  # never fires

# RIGHT — declare at class scope
@editable
MyButton : button_device = button_device{}

2. SetInteractionText requires a message, not a string

The parameter type is message (a localized type). Passing a raw string literal causes a compile error. Always wrap with a <localizes> helper:

# WRONG
MyButton.SetInteractionText("Open Door")  # compile error: string ≠ message

# RIGHT
DoorPrompt<localizes>(S : string) : message = "{S}"
MyButton.SetInteractionText(DoorPrompt("Open Door"))

3. GetTriggerCountRemaining returns 0 for unlimited buttons

If GetMaxTriggerCount() returns 0 (meaning no limit), then GetTriggerCountRemaining() also returns 0 — not a large number. Always check GetMaxTriggerCount() first before interpreting the remaining count:

if (MyButton.GetMaxTriggerCount() = 0):
    # Unlimited — remaining count is meaningless
else:
    Remaining := MyButton.GetTriggerCountRemaining()
    # Safe to use Remaining here

4. InteractedWithEvent fires AFTER the full hold time

The event does not fire on button-down — it fires only after the player holds for the full GetInteractionTime() duration. If you call SetInteractionTime(5.0), your handler won't run until 5 seconds of uninterrupted holding. Design your UX accordingly — if the time is too long, players will assume the button is broken.

5. SetMaxTriggerCount clamps to 0–10000

Passing a value outside [0, 10000] is clamped. Pass 0 explicitly for unlimited. There is no -1 sentinel — using negative values is undefined behavior.

6. Event handlers are class-scope methods, not lambdas

Subscribe takes a method reference. Define your handler as a method on the class (not inside OnBegin) and pass it by name:

# WRONG — anonymous function inside OnBegin won't compile as a subscriber
MyButton.InteractedWithEvent.Subscribe((A : agent) => { ... })

# RIGHT — class-scope method
OnPressed(Interactor : agent) : void =
    # your logic here

OnBegin<override>()<suspends> : void =
    MyButton.InteractedWithEvent.Subscribe(OnPressed)

Device Settings & Options

The button_device User Options panel in the UEFN editor — every setting you can tune.

Button Device settings and options panel in the UEFN editor — Interact Time, Activating Team, Trigger Sound, Interaction Text
Button Device — User Options in the UEFN editor: Interact Time, Activating Team, Trigger Sound, Interaction Text
⚙️ Settings on this device (4)

Setting names read from the panel above — may be partial; the screenshots are the source of truth.

Interact Time
Activating Team
Trigger Sound
Interaction Text

Guides & scripts that use button_device

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

Build your own lesson with button_device

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 →