Overview
A guard clause is a check placed at the very top of a handler that validates its inputs and exits early if something is wrong. Instead of nesting your real logic inside deep if pyramids, you flip the condition: "if the thing I need isn't here, stop now." What's left below the guards is code that runs only when all preconditions hold.
In Verse this maps perfectly onto failable expressions (if (X := Expr):) and the option unwrap. The classic case is a trigger_device: its TriggeredEvent is a listenable(?agent), meaning it may hand you an agent — or false when the device was fired from code with the no-argument Trigger(). You must guard against that before touching the player.
Reach for guard-clause validation whenever a device event depends on state that might not be valid: a cannon that should only fire while it has ammo (GetTriggerCountRemaining), a plank that must be Enabled, or an agent-only trap that must ignore ghost triggers. Our sunny-cove scenario: a plank on a pirate ship that arms a clifftop cannon — but only for real players, and only while shots remain.
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):
vector3
3-dimensional vector with
floatcomponents.
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse).
vector3<native><public> := struct<concrete><computes><persistable><uht_comparable>:
Walkthrough
Here's the full scene. A PlankPlate trigger sits on the pirate ship's gangplank. When a pirate walks it, we validate the agent, confirm the cannon still has shots left, and only then fire the CannonTrigger — which is linked to the actual cannon VFX/damage device in the editor. Each guard clause is a one-line early exit.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
cove_cannon_device := class(creative_device):
# The plank the player steps on.
@editable
PlankPlate : trigger_device = trigger_device{}
# The cannon: firing it means calling Trigger on this linked device.
@editable
CannonTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
# Cannon starts with a limited magazine and a cooldown.
CannonTrigger.SetMaxTriggerCount(5)
CannonTrigger.SetResetDelay(1.5)
# React to a pirate stepping on the plank.
PlankPlate.TriggeredEvent.Subscribe(OnPlankStepped)
OnPlankStepped(Agent : ?agent) : void =
# GUARD 1: bail if no real agent triggered this (code trigger sends false).
if (StepperAgent := Agent?):
FireCannonFor(StepperAgent)
FireCannonFor(Firer : agent) : void =
# GUARD 2: bail if the cannon is out of ammo.
Remaining := CannonTrigger.GetTriggerCountRemaining()
if (Remaining > 0):
# All guards passed — do the real work.
CannonTrigger.Trigger(Firer)
Line by line:
PlankPlateandCannonTriggerare two placedtrigger_devices exposed with@editable. You can't call methods on a device unless it's an editable field on thecreative_deviceclass — a bareTrigger()would fail with Unknown identifier.- In
OnBegin,SetMaxTriggerCount(5)gives the cannon a 5-shot magazine (clamped 0–20;0means unlimited).SetResetDelay(1.5)forces a 1.5-second cooldown between shots. PlankPlate.TriggeredEvent.Subscribe(OnPlankStepped)wires the plank's event to our handler method.- GUARD 1 lives in
OnPlankStepped. The handler receivesAgent : ?agent— an option.if (StepperAgent := Agent?):succeeds only when a genuine agent is present. ATrigger()fired from code (no agent) sendsfalse, so this guard silently rejects ghost triggers. - GUARD 2 lives in
FireCannonFor. We readGetTriggerCountRemaining()and only proceed if it's above zero. Once the magazine is empty the cannon simply won't fire. - Only after both guards pass do we call
CannonTrigger.Trigger(Firer), passing the validated agent so the cannon fires for that specific pirate.
Common patterns
Pattern 1 — Enable/Disable as a state guard. Instead of checking a boolean flag, use the device's own enabled state. Here a lever Trigger arms the plank; before that it's Disabled so steps do nothing.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_arming_device := class(creative_device):
@editable
LeverTrigger : trigger_device = trigger_device{}
@editable
PlankPlate : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
# Plank is inert until the lever is pulled.
PlankPlate.Disable()
LeverTrigger.TriggeredEvent.Subscribe(OnLeverPulled)
OnLeverPulled(Agent : ?agent) : void =
# Guard: only a real pirate can arm the trap.
if (Puller := Agent?):
PlankPlate.Enable()
Pattern 2 — Reject the shot when transmit delay hasn't been configured. You can read back a setter's value and guard on it. Here we only fire if the cannon's reset delay is long enough to be "safe" (a silly but illustrative validation), and we tune transmit delay first.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_delay_guard_device := class(creative_device):
@editable
CannonTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
CannonTrigger.SetTransmitDelay(0.5)
CannonTrigger.SetResetDelay(2.0)
# Guard on configured state before ever firing.
Reset := CannonTrigger.GetResetDelay()
Transmit := CannonTrigger.GetTransmitDelay()
if (Reset >= 1.0, Transmit >= 0.0):
# Both conditions in one guard — fire the opening salvo.
CannonTrigger.Trigger()
Pattern 3 — Compare against the magazine size. Use GetMaxTriggerCount alongside GetTriggerCountRemaining to detect "first shot" vs "last shot" and guard accordingly.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_last_shot_device := class(creative_device):
@editable
PlankPlate : trigger_device = trigger_device{}
@editable
CannonTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
CannonTrigger.SetMaxTriggerCount(3)
PlankPlate.TriggeredEvent.Subscribe(OnStepped)
OnStepped(Agent : ?agent) : void =
if (Firer := Agent?):
Max := CannonTrigger.GetMaxTriggerCount()
Remaining := CannonTrigger.GetTriggerCountRemaining()
# Guard: only fire while shots remain, and give the final salvo priority.
if (Remaining > 0):
CannonTrigger.Trigger(Firer)
Gotchas
?agentis an option, not an agent.TriggeredEventhands youAgent : ?agent. You must unwrap withif (A := Agent?):before using it. Skipping this is the #1 guard-clause mistake — and it's exactly the case where a code-sideTrigger()(no agent) sendsfalse.Trigger()vsTrigger(Agent). The no-arg overload fires the event with no agent, so downstream?agentguards will reject it. If you need the event consumer to know who fired, always pass an agent withTrigger(Firer).GetTriggerCountRemainingreturns 0 for unlimited. If you setSetMaxTriggerCount(0)(unlimited),GetTriggerCountRemaining()also returns0. A naiveif (Remaining > 0):guard would then never fire. Guard onGetMaxTriggerCount()too if you allow unlimited magazines.SetMaxTriggerCountis clamped 0–20. Passing50silently becomes20. Read it back withGetMaxTriggerCount()if the exact value matters to your logic.- Verse doesn't auto-convert int↔float.
GetTriggerCountRemaining()returnsint;GetResetDelay()returnsfloat. Don't compare or mix them without an explicit conversion. - Guards must be early exits, not deep nesting. The point is that the failable
ifwraps the rest of the work — anything after a failed guard simply doesn't run. Keep validated logic flat and readable.