Reference Verse compiles

trigger_device: The Signal Relay of Your Island

The trigger_device is the humble messenger of UEFN — it senses a player and fires a signal that any other device can react to. In this guide we build a sunny pirate cove where stepping onto the dock plank kicks off a whole chain of island events, all driven by one trigger.

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

Overview

On a sunny cove finish line, you rarely care about the whole crowd — you care about the first person who qualifies. Maybe it's the first player standing on the dock, the first agent whose score cleared a threshold, or the first teleporter that's still enabled. That's exactly what Verse's first expression does: it iterates a collection, tests each element with a <decides> condition, and returns the very first element that succeeds — then stops looking.

first is not a device — it's a core Verse control-flow expression (a cousin of for). Because it can fail (there might be no match), you call it inside a failable context like if (Winner := first(...)):. In this article we pair it with real devices — a damage_volume_device used as a trigger zone to gather agents, a cinematic_sequence_device to play the win cutscene, and a teleporter_device to send the winner home — so the abstract idea lands as a concrete game moment.

Reach for first whenever the answer is "the earliest element that qualifies": first ready player, first free spawner, first enabled teleporter. Use for when you need all matches instead.

API Reference

(API surface could not be resolved for this device.)

Walkthrough

The scene: a wooden dock on a bright cel-shaded shore. A large damage_volume_device (with damage set to 0 so it acts as a harmless detection zone) covers the end of the dock. When any player runs into it, we scan the agents currently in the zone and grab the first one — that's our winner. We play the victory cinematic for them and teleport them to a champion's platform.

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

dock_finish_line := class(creative_device):

    # The detection zone at the end of the dock. Set its damage to 0 in the editor.
    @editable
    FinishZone : damage_volume_device = damage_volume_device{}

    # The victory cutscene that plays for the winner.
    @editable
    WinCinematic : cinematic_sequence_device = cinematic_sequence_device{}

    # Teleporter that whisks the winner to the champion platform.
    WinnerTeleporter : teleporter_device = teleporter_device{}

    # Have we already crowned a winner this round?
    var WinnerCrowned : logic = false

    OnBegin<override>()<suspends>:void =
        # Make sure the zone deals no damage — it's just a trip-wire.
        FinishZone.SetDamage(0)
        # Listen for anyone entering the finish zone.
        FinishZone.AgentEntersEvent.Subscribe(OnAgentEntered)

    OnAgentEntered(Agent : agent) : void =
        # Only crown one winner per round.
        if (not WinnerCrowned?):
            CrownFirstArrival()

    CrownFirstArrival() : void =
        # Everyone currently standing in the zone.
        AgentsHere := FinishZone.GetAgentsInVolume()
        # We simply grab the first agent in the volume.
        if (Winner := AgentsHere[0]):
            set WinnerCrowned = true
            # Reward the winner with the cutscene and a teleport.
            WinCinematic.Play(Winner)
            WinnerTeleporter.Teleport(Winner)```

**Line by line:**

- The three `@editable` fields let you drop the device into the level and wire the real placed `damage_volume_device`, `cinematic_sequence_device`, and `teleporter_device` in the Details panel.
- `var WinnerCrowned : logic = false` is our guard so a whole stampede doesn't re-trigger the cutscene.
- In `OnBegin`, `FinishZone.SetDamage(0)` turns the damage volume into a harmless trip-wire, then we `Subscribe` our handler to `AgentEntersEvent`.
- `OnAgentEntered` receives the entering `agent` directly (this event's payload is a plain `agent`, not `?agent`). We only proceed if no winner has been crowned yet.
- `GetAgentsInVolume()` returns `[]agent`. The `first (A : AgentsHere): A` expression iterates that array; the body `A` always succeeds, so `first` returns the *earliest* agent in the list. The `if (Winner := ...)` unwraps it  if the array were empty, `first` fails and the block is skipped.
- With a `Winner`, we flip the guard, call `WinCinematic.Play(Winner)` (the agent overload, required because the cinematic is set to a specific instigator), and `WinnerTeleporter.Teleport(Winner)`.

## Common patterns

**1. First player over a score threshold.** Pair `first` with a real device by using the winner's agent to grant points and stop a `timer_device`. Here we scan the zone's occupants for the first agent, then reward them.

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

first_to_finish := class(creative_device):

    @editable
    Zone : damage_volume_device = damage_volume_device{}

    @editable
    RaceTimer : timer_device = timer_device{}

    @editable
    ScoreAward : score_manager_device = score_manager_device{}

    OnBegin<override>()<suspends>:void =
        Zone.SetDamage(0)
        Zone.AgentEntersEvent.Subscribe(OnEntered)

    OnEntered(Agent : agent) : void =
        Occupants := Zone.GetAgentsInVolume()
        # Grab the first agent standing on the finish pad.
        if (Champ := first(A : Occupants): A):
            # Stop the race clock and hand the champ their points.
            RaceTimer.Reset(Champ)
            ScoreAward.Activate(Champ)

2. First enabled teleporter fallback. When you keep a list of teleporters, first lets you pick one to send a spawned player to. This snippet grabs the first teleporter in an array and links it back.

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

spawn_router := class(creative_device):

    @editable
    Spawner : player_spawner_device = player_spawner_device{}

    # Wire several teleporters here in the editor.
    @editable
    Exits : []teleporter_device = array{}

    OnBegin<override>()<suspends>:void =
        Spawner.SpawnedEvent.Subscribe(OnSpawned)

    OnSpawned(Agent : agent) : void =
        # Route the freshly spawned agent through the first available exit.
        if (Exit := first(T : Exits): T):
            Exit.ActivateLinkToTarget()
            Exit.Teleport(Agent)

3. First player reference match. first combined with a player_reference_device's IsReferenced check finds the earliest agent in a zone that the device is tracking, then activates the cinematic for them.

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

tracked_arrival := class(creative_device):

    @editable
    Zone : damage_volume_device = damage_volume_device{}

    @editable
    TrackedRef : player_reference_device = player_reference_device{}

    @editable
    Cutscene : cinematic_sequence_device = cinematic_sequence_device{}

    OnBegin<override>()<suspends>:void =
        Zone.SetDamage(0)
        Zone.AgentEntersEvent.Subscribe(OnEntered)

    OnEntered(Agent : agent) : void =
        Occupants := Zone.GetAgentsInVolume()
        # first + a <decides> body: keep only the tracked agent.
        if (VIP := first(A : Occupants): TrackedRef.IsReferenced[A]; A):
            Cutscene.Play(VIP)

Gotchas

  • first can fail. If the collection is empty or nothing matches, the whole expression fails — so it must live in a failable context (if (X := first(...)):, for, or a <decides> function). Don't try to bind its result unconditionally.
  • The body decides the match. In first(A : Xs): SomeCheck[A]; A, the semicolon-separated body runs as a failable block. If SomeCheck[A] fails, that element is skipped and first moves on. The final expression (A) is what gets returned.
  • first returns the element, not an index. Unlike a manual loop counter, you get the value directly. If you need position, iterate with for and track it yourself.
  • Event payload types differ. damage_volume_device.AgentEntersEvent hands you a plain agent — no ? to unwrap. But timer_device.SuccessEvent is listenable(?agent); that one needs if (A := MaybeAgent?): before use. Match your handler signature to the real event type.
  • Instigator overloads. cinematic_sequence_device.Play(Agent) and teleporter_device.Teleport(Agent) are the agent overloads — use them when the device is set to anything other than Everyone. Calling the no-arg Play() on an instigator-scoped device silently does nothing.
  • Damage volume as a trigger. A damage_volume_device with SetDamage(0) is a handy detection zone, but remember its default damage may be nonzero in the editor — call SetDamage(0) in OnBegin if you only want detection.

Guides & scripts that use player

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

Build your own lesson with player

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 →