Reference Verse compiles

Keep OnBegin Light: The trigger_device Relay Pattern

It's a bright, cel-shaded morning on the cove dock — gulls wheeling, sun glittering on the lagoon. A pirate steps onto the plank and a treasure chest pops open. The secret? A `trigger_device` doing all the heavy lifting so your `OnBegin` stays tiny and snappy. This article shows how to wire it up the light way.

Updated Examples verified on the live UEFN compiler
Watch the Knottrigger_device in ~90 seconds.

Overview

When your island loads, OnBegin runs once per Verse device. It is tempting to cram everything in there — spawn logic, loops, timers, subscriptions to a dozen devices. That makes startup heavy and hard to read. The golden rule is keep OnBegin light: do only the minimum wiring (grab references, subscribe to events), then let the devices handle the runtime work.

The trigger_device is the perfect partner for this pattern. It is a tiny relay: something signals it (a player steps on it, or your code calls Trigger), and it fires its TriggeredEvent to whatever is listening. Because it has built-in reset delays, transmit delays, and a max trigger count, you can push cooldowns and one-shot logic onto the device instead of writing timer loops in Verse.

Reach for it when you want a clean, event-driven island: a chest that opens when a pirate walks the plank, a cannon that only fires three times, a dock bell that can't be spammed. Your OnBegin just subscribes and configures — then gets out of the way.

API Reference

trigger_device

Used to relay events to other linked devices.

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

trigger_device<public> := class<concrete><final>(trigger_base_device):

Events (subscribe a handler to react):

Event Signature Description
TriggeredEvent TriggeredEvent<public>:listenable(?agent) Signaled when an agent triggers this device. Sends the agent that used this device. Returns false if no agent triggered the action (ex: it was triggered through code).

Methods (call these to make the device act):

Method Signature Description
Trigger Trigger<public>(Agent:agent):void Triggers this device with Agent being passed as the agent that triggered the action. Use an agent reference when this device is setup to require one (for instance, you want to trigger the device only with a particular agent.
Trigger Trigger<public>():void Triggers this device, causing it to activate its TriggeredEvent event.
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
SetMaxTriggerCount SetMaxTriggerCount<public>(MaxCount:int):void Sets the maximum amount of times this device can trigger. * 0 can be used to indicate no limit on trigger count. * MaxCount is clamped between [0,20].
GetMaxTriggerCount GetMaxTriggerCount<public>()<transacts>:int Gets the maximum amount of times this device can trigger. * 0 indicates no limit on trigger count.
GetTriggerCountRemaining GetTriggerCountRemaining<public>()<transacts>:int Returns the number of times that this device can still be triggered before hitting GetMaxTriggerCount. Returns 0 if GetMaxTriggerCount is unlimited.
SetResetDelay SetResetDelay<public>(Time:float):void Sets the time (in seconds) after triggering, before the device can be triggered again (if MaxTrigger count allows).
GetResetDelay GetResetDelay<public>()<transacts>:float Gets the time (in seconds) before the device can be triggered again (if MaxTrigger count allows).
SetTransmitDelay SetTransmitDelay<public>(Time:float):void Sets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.
GetTransmitDelay GetTransmitDelay<public>()<transacts>:float Gets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.

player

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

player<native><public> := class<unique><persistent><module_scoped_var_weak_map_key><epic_internal>(agent):

Walkthrough

Here is the sunny cove scene. A trigger_device sits on the end of the dock plank. When a pirate steps on it, it fires TriggeredEvent. We listen for that and play a treasure-chest cinematic. But we keep OnBegin light: all we do there is configure the trigger (limit it to 3 opens, give it a 5-second cooldown) and subscribe once. No loops, no waiting.

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

# The dock chest: a trigger on the plank opens a cinematic chest.
dock_chest_device := class(creative_device):

    # The trigger physically placed on the end of the plank.
    @editable
    PlankTrigger : trigger_device = trigger_device{}

    # The cinematic that plays the lid-pop animation.
    @editable
    ChestCinematic : cinematic_sequence_device = cinematic_sequence_device{}

    # OnBegin stays LIGHT: configure once, subscribe once, done.
    OnBegin<override>()<suspends>:void =
        # The chest may only be opened 3 times this match.
        PlankTrigger.SetMaxTriggerCount(3)
        # After firing, wait 5s before it can fire again (built-in cooldown).
        PlankTrigger.SetResetDelay(5.0)
        # Make sure the plank is live.
        PlankTrigger.Enable()
        # React to a pirate stepping on the plank.
        PlankTrigger.TriggeredEvent.Subscribe(OnPlankStepped)

    # Event handler is a METHOD at class scope, not inside OnBegin.
    # A listenable(?agent) hands us an optional agent.
    OnPlankStepped(Agent : ?agent):void =
        # Unwrap the optional agent before using it.
        if (Pirate := Agent?):
            # Play the chest-opening cinematic for that pirate.
            ChestCinematic.Play(Pirate)
            # Report how many opens are left on the device itself.
            Remaining := PlankTrigger.GetTriggerCountRemaining()
            Print("Chest opened! Opens remaining: {Remaining}")

Line by line:

  • The @editable fields let you drop the placed trigger_device and cinematic_sequence_device into the slots in UEFN. You must declare them as fields — calling trigger_device{}.Trigger() on a bare value would not point at your placed device.
  • OnBegin<override>()<suspends>:void = runs once at startup. Everything inside is cheap: three configuration calls plus one subscription.
  • SetMaxTriggerCount(3) tells the device to stop firing after 3 uses — no Verse counter needed. MaxCount is clamped to [0,20], where 0 means unlimited.
  • SetResetDelay(5.0) puts a 5-second cooldown on the device. This is the essence of keeping OnBegin light: the device enforces the cooldown, not a Sleep loop in your code.
  • TriggeredEvent.Subscribe(OnPlankStepped) wires the plank to our handler. The subscription is the only ongoing work OnBegin sets up.
  • OnPlankStepped(Agent : ?agent) is the handler. Because TriggeredEvent is listenable(?agent), the parameter is an optional agent. We unwrap with if (Pirate := Agent?) before using it.
  • ChestCinematic.Play(Pirate) runs the lid-pop cinematic for the instigating pirate. GetTriggerCountRemaining() reads live state straight off the device.

Common patterns

Fire the trigger from code (no plank step needed)

Sometimes you want the chest to open from another event — say a timer, or a boss defeat. trigger_device has a parameterless Trigger() that fires TriggeredEvent directly. Note: when triggered purely from code, the ?agent sent is empty, so guard your unwrap.

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

code_fired_bell_device := class(creative_device):

    @editable
    DockBell : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        DockBell.Enable()
        DockBell.TriggeredEvent.Subscribe(OnBellRung)
        # Ring the bell from code — no player needed.
        DockBell.Trigger()

    OnBellRung(Agent : ?agent):void =
        if (Ringer := Agent?):
            Print("A pirate rang the dock bell!")
        else:
            Print("The dock bell rang on its own at dawn.")

Anti-spam bell with a transmit delay

Use SetTransmitDelay to hold back informing linked devices for a moment after the trigger fires — great for staggering an effect. Here the shore cannon waits 2 seconds before it tells its cinematic to fire, so a countdown feels right.

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

shore_cannon_device := class(creative_device):

    @editable
    FuseTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        # Wait 2s after triggering before relaying to linked devices.
        FuseTrigger.SetTransmitDelay(2.0)
        FuseTrigger.Enable()
        Delay := FuseTrigger.GetTransmitDelay()
        Print("Cannon fuse set. Transmit delay is {Delay} seconds.")
        FuseTrigger.TriggeredEvent.Subscribe(OnFuseLit)

    OnFuseLit(Agent : ?agent):void =
        Print("Fuse lit! Cannonball incoming in a moment...")

Disable a used-up gangplank

Once a one-shot event happens, Disable() the trigger so it stops responding — cheaper and clearer than tracking a boolean. This lagoon rift only lets the first pirate through.

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

one_way_rift_device := class(creative_device):

    @editable
    RiftTrigger : trigger_device = trigger_device{}

    @editable
    LagoonTeleporter : teleporter_device = teleporter_device{}

    OnBegin<override>()<suspends>:void =
        RiftTrigger.Enable()
        RiftTrigger.TriggeredEvent.Subscribe(OnRiftEntered)

    OnRiftEntered(Agent : ?agent):void =
        if (Pirate := Agent?):
            LagoonTeleporter.Activate(Pirate)
            # First pirate only — close the rift behind them.
            RiftTrigger.Disable()
            Print("The lagoon rift closes behind the first pirate.")

Gotchas

  • Keep OnBegin light — for real. Never put long loop/Sleep chains in OnBegin just to build a cooldown. Use SetResetDelay / SetTransmitDelay on the trigger, or SetMaxTriggerCount. The device does the waiting for you and startup stays instant.
  • TriggeredEvent sends ?agent, not agent. You must unwrap with if (Pirate := Agent?): before using it. When the device is fired by code (Trigger() with no arg), that optional can be empty — handle the else case.
  • You must declare the device as an @editable field. A bare trigger_device{}.Enable() in a local won't touch your placed device and effectively does nothing useful. Wire the field in UEFN.
  • Event handlers are methods, not nested functions. Define OnPlankStepped at class indentation (4 spaces), and Subscribe to it inside OnBegin. Defining it inside OnBegin will not compile.
  • SetMaxTriggerCount is clamped to [0,20]. Passing 0 means unlimited, not zero — if you want the trigger disabled, call Disable() instead.
  • GetTriggerCountRemaining returns 0 when max is unlimited. Don't read a 0 as "used up" if you never set a max count.
  • No int↔float auto-convert. SetResetDelay takes a float — pass 5.0, never 5.

Guides & scripts that use trigger_device

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

Build your own lesson with trigger_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 →