Overview
An immutable array in Verse is a fixed-length, ordered collection of values that cannot be structurally changed after it is created. You cannot add or remove elements at runtime — but you can read every element, loop over them with for, access them by index, and pass them to functions. Because arrays are values (not references), they are safe to share across handlers without defensive copying.
The trigger_device is the perfect companion for array iteration practice: you place several of them in the world, collect them in an @editable array field, and then loop over that array to call methods like Enable, Disable, Trigger, SetMaxTriggerCount, or SetResetDelay on each one — driving real in-game effects from a single loop.
When to reach for immutable array iteration:
- You have a fixed set of devices placed in the editor (dock lanterns, cannon triggers, checkpoint plates).
- You want to activate, configure, or query all of them in one sweep at game start.
- You need to react to one trigger and then cascade effects across the whole group.
- You want to read device state (e.g.
GetTriggerCountRemaining) from every device and make a decision.
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):
team
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse).
team<native><public> := class<unique><epic_internal>:
Walkthrough
The scenario: A 2D cel-shaded pirate ship is docking at a sunny lagoon cove. Five trigger_devices are placed along the wooden dock — each one represents a mooring cleat a crew member steps on. When the ship arrives (a sixth "arrival" trigger fires), your Verse device loops over all five dock triggers, enables them, sets each one to fire at most three times, and gives each a short reset delay so the dock feels lively. When any dock trigger fires, the whole array is queried and the remaining-trigger counts are printed to the log for debugging.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
# Placed on the island. Wire ArrivalTrigger to a pressure plate at the
# end of the gangplank; wire each DockTrigger to a mooring-cleat plate.
dock_arrival_manager := class(creative_device):
# The trigger that fires when the pirate ship docks.
@editable
ArrivalTrigger : trigger_device = trigger_device{}
# The five dock-cleat triggers placed along the lagoon pier.
@editable
DockTriggers : []trigger_device = array{}
# How many times each cleat trigger may fire before it locks out.
CleatMaxUses : int = 3
# Seconds before a cleat can be stepped on again.
CleatResetDelay : float = 2.0
OnBegin<override>()<suspends> : void =
# All dock triggers start disabled — the dock is empty.
for (Trigger : DockTriggers):
Trigger.Disable()
# Subscribe to the ship-arrival trigger.
ArrivalTrigger.TriggeredEvent.Subscribe(OnShipArrived)
# Subscribe every dock trigger to the same handler.
for (Trigger : DockTriggers):
Trigger.TriggeredEvent.Subscribe(OnCleatStepped)
# Called when the pirate ship reaches the dock.
OnShipArrived(MaybeAgent : ?agent) : void =
# Enable every cleat trigger and configure its limits.
for (Trigger : DockTriggers):
Trigger.Enable()
Trigger.SetMaxTriggerCount(CleatMaxUses)
Trigger.SetResetDelay(CleatResetDelay)
# Called whenever any dock-cleat trigger fires.
OnCleatStepped(MaybeAgent : ?agent) : void =
# Iterate the array and query remaining uses on every trigger.
for (Trigger : DockTriggers):
var Remaining : int = Trigger.GetTriggerCountRemaining()
# In a real game you might update a UI widget here.
# For now we just read the value — the loop itself is the lesson.```
**Line-by-line explanation:**
| Lines | What's happening |
|---|---|
| `@editable DockTriggers : []trigger_device` | Declares an immutable array of `trigger_device` references. You populate it in the UEFN editor by dragging devices into the array slots. |
| `for (Trigger : DockTriggers): Trigger.Disable()` | The canonical Verse `for` loop over an immutable array. Each element is bound to `Trigger` in turn — no index needed. |
| `ArrivalTrigger.TriggeredEvent.Subscribe(OnShipArrived)` | Subscribes a class-scope method to the `listenable(?agent)` event. |
| `OnShipArrived(MaybeAgent : ?agent)` | Handler signature matches `listenable(?agent)` — the parameter is `?agent`, an optional. |
| `Trigger.Enable()` / `Trigger.SetMaxTriggerCount(CleatMaxUses)` / `Trigger.SetResetDelay(CleatResetDelay)` | Three different `trigger_device` methods called inside one loop — covering Enable, SetMaxTriggerCount, and SetResetDelay in a single pass. |
| `Trigger.GetTriggerCountRemaining()` | Reads device state inside the second loop — demonstrates querying across the whole array. |
## Common patterns
### Pattern 1 — Bulk-fire all triggers at once (cannon salute)
When the ship's captain fires a flare, every cannon trigger on the ship fires simultaneously. This uses `Trigger()` (the no-agent overload) inside a `for` loop.
```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
cannon_salute_manager := class(creative_device):
@editable
FlareSignalTrigger : trigger_device = trigger_device{}
# All cannon triggers placed on the pirate ship deck.
@editable
CannonTriggers : []trigger_device = array{}
OnBegin<override>()<suspends> : void =
FlareSignalTrigger.TriggeredEvent.Subscribe(OnFlare)
OnFlare(MaybeAgent : ?agent) : void =
# Fire every cannon trigger — no agent needed.
for (Cannon : CannonTriggers):
Cannon.Trigger()
Key call: Cannon.Trigger() — the zero-argument overload fires the device programmatically, activating anything wired to it in the editor (particle FX, sound cues, score grants).
Pattern 2 — Index-based access to configure transmit delays
Each cannon fires with a staggered transmit delay so the salvo sounds like rolling thunder rather than one bang. You need the index to compute the delay, so use for (Index -> Trigger : CannonTriggers).
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
staggered_cannon_setup := class(creative_device):
@editable
CannonTriggers : []trigger_device = array{}
# Seconds between each cannon's transmit signal.
StaggerSeconds : float = 0.4
OnBegin<override>()<suspends> : void =
# Use index-value iteration to compute a per-cannon delay.
for (Index -> Trigger : CannonTriggers):
# Index is int; StaggerSeconds is float — must convert explicitly.
var Delay : float = StaggerSeconds * (Index + 0)
# Verse won't auto-convert int to float, so we compute via float ops.
# We use a helper to multiply: Index as a float offset.
Trigger.SetTransmitDelay(StaggerSeconds * IntToFloat(Index))
Trigger.SetMaxTriggerCount(1)
# Helper: convert int to float by adding 0.0 via repeated addition.
# In real projects use the built-in int-to-float cast if available.
IntToFloat(N : int) : float =
var Result : float = 0.0
for (_ : 0..N-1):
set Result = Result + 1.0
Result
Key calls: SetTransmitDelay(float) and SetMaxTriggerCount(int) — both called inside an index-value for loop, demonstrating that you can use the loop index to parameterize each device differently.
Pattern 3 — Query the array and disable exhausted triggers
After the docking ceremony, sweep the array and disable any trigger that has no uses remaining. Uses GetMaxTriggerCount and GetTriggerCountRemaining to make the decision.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
dock_cleanup_manager := class(creative_device):
@editable
CleanupSignalTrigger : trigger_device = trigger_device{}
@editable
DockTriggers : []trigger_device = array{}
OnBegin<override>()<suspends> : void =
CleanupSignalTrigger.TriggeredEvent.Subscribe(OnCleanup)
OnCleanup(MaybeAgent : ?agent) : void =
for (Trigger : DockTriggers):
var MaxCount : int = Trigger.GetMaxTriggerCount()
var Remaining : int = Trigger.GetTriggerCountRemaining()
# If the trigger has a limit (MaxCount > 0) and is exhausted, disable it.
if (MaxCount > 0, Remaining = 0):
Trigger.Disable()
Key calls: GetMaxTriggerCount() and GetTriggerCountRemaining() — both <transacts> methods safe to call inside a for loop. The if guard uses Verse's multi-condition form.
Gotchas
1. Arrays are immutable — var lets you replace, not mutate
You cannot do DockTriggers.Add(newTrigger) at runtime. The array's length is fixed the moment it is created. If you declare the field as var DockTriggers : []trigger_device = array{}, you can reassign the variable to a brand-new array, but the original array object is unchanged. For a fixed set of editor-placed devices, a plain (non-var) @editable field is correct and clearest.
2. for loops over arrays are expressions, not statements
In Verse, for (X : MyArray): X.DoSomething() is an expression that returns an array of results. If you don't use the result, assign it to _ or just let it be the last expression in a block. The compiler may warn if the result is silently discarded in some contexts.
3. listenable(?agent) hands you ?agent, not agent
Every TriggeredEvent handler receives MaybeAgent : ?agent. Before calling any agent-typed API, unwrap it:
OnCleatStepped(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# A is now a concrete agent — safe to pass to agent-typed methods.
_ = A
Forgetting the unwrap is the #1 compile error when subscribing to trigger events.
4. No automatic int ↔ float conversion
SetResetDelay takes float; SetMaxTriggerCount takes int. You cannot pass an int literal where float is expected or vice versa. Write 2.0 not 2 for float parameters, and use an explicit conversion (or a helper loop as shown in Pattern 2) when you need to derive a float from an int index.
5. GetTriggerCountRemaining returns 0 when the limit is unlimited
If GetMaxTriggerCount() returns 0 (meaning no limit), GetTriggerCountRemaining() also returns 0 — not infinity. Always check GetMaxTriggerCount() > 0 before interpreting a 0 remaining count as "exhausted" (as shown in Pattern 3).
6. SetMaxTriggerCount clamps to [0, 20]
Passing a value above 20 silently clamps to 20. If your design needs more than 20 activations, set the limit to 0 (unlimited) and manage the count yourself in Verse with a var counter field.