Reference Verse compiles

Early Return Pattern: Writing Cleaner Verse Logic

When your game logic needs to check several conditions before doing something meaningful — like only rewarding a player who is alive, on the dock, and holding the right item — nested `if` blocks get messy fast. The early return pattern (also called a guard clause) lets you bail out of a function the moment a condition fails, keeping the happy path flat and readable. This article teaches the pattern through a sunny Verse Island cove scenario where a lighthouse keeper rewards players who reach the

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

Overview

The early return pattern is not a device — it is a Verse coding technique. Instead of nesting all your conditions inside one deep if block, you check each precondition at the top of a function and return (or return a value) the moment one fails. Everything after that check is guaranteed to run only when the condition passed.

In UEFN Verse this matters because:

  • agent values are often wrapped in ?agent (option types) and must be unwrapped before use.
  • Many API calls are failable (they use <decides>) and must appear inside if expressions.
  • Game events fire for every player — you frequently need to filter by team, health, zone, or inventory before acting.

When to reach for it: any handler that has two or more guard conditions before the real work begins.

API Reference

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

Walkthrough

Scenario — The Clifftop Dock Reward

Sunny cove, 2D cel-shaded. A pressure plate sits on the clifftop dock. When a player steps on it the lighthouse keeper checks:

  1. Was a real agent passed? (unwrap ?agent)
  2. Is the agent actually a fort_character? (failable cast)
  3. Is the character still alive? (IsActive[] — failable)

Only if all three pass does the keeper grant an item and enable a barrier device that opens the sea-gate.

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

# Place a trigger_device (the dock pressure plate),
# an item_granter_device (the keeper's reward),
# and a barrier_device (the sea-gate) in your UEFN level
# and wire them to these @editable fields.
dock_reward_manager := class(creative_device):

    @editable
    DockPlate : trigger_device = trigger_device{}

    @editable
    RewardGranter : item_granter_device = item_granter_device{}

    @editable
    SeaGate : barrier_device = barrier_device{}

    # Entry point — subscribe to the plate's event.
    OnBegin<override>()<suspends> : void =
        # Sea-gate starts closed.
        SeaGate.Enable()
        # Listen for any player stepping on the dock plate.
        DockPlate.TriggeredEvent.Subscribe(OnDockTriggered)

    # Called every time the plate fires.
    # The event passes ?agent, so we must unwrap it.
    OnDockTriggered(MaybeAgent : ?agent) : void =
        # GUARD 1 — was there actually an agent?
        # Early return if the option is empty.
        if (A := MaybeAgent?):
            HandleDockedPlayer(A)

    # All real logic lives here, behind clean guards.
    HandleDockedPlayer(A : agent) : void =
        # GUARD 2 — can we get a fort_character from this agent?
        # GetFortCharacter[] is failable (<decides>), so it lives in an if.
        if (Character := A.GetFortCharacter[]):
            # GUARD 3 — is the character still alive?
            # IsActive[] is also failable.
            if (Character.IsActive[]):
                # Happy path — all guards passed.
                # Grant the lighthouse keeper's reward.
                RewardGranter.GrantItem(A)
                # Open the sea-gate for this player.
                SeaGate.Disable()

Line-by-line explanation

Lines What's happening
@editable fields Lets you wire the plate, granter, and gate in the UEFN editor without touching code.
SeaGate.Enable() Closes the barrier at game start so the gate is locked.
DockPlate.TriggeredEvent.Subscribe(OnDockTriggered) Registers the handler; the event signature is listenable(?agent).
OnDockTriggered(MaybeAgent : ?agent) Matches the event's parameter type exactly.
if (A := MaybeAgent?) Guard 1 — unwraps the option. If it's false (no agent), the function exits here.
if (Character := A.GetFortCharacter[]) Guard 2 — failable call; exits if the agent has no character.
if (Character.IsActive[]) Guard 3 — exits if the character is already eliminated.
RewardGranter.GrantItem(A) Only reached when every guard passed — the happy path.

Common patterns

Pattern 1 — Flat guards with early return in a helper function

Instead of nesting three if blocks you can chain guards in one if expression using , (logical AND in Verse). This is the flattest possible form.

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

# A shore-side chest opener: only living players on the correct team
# can open the treasure chest (represented by an item_granter_device).
shore_chest_opener := class(creative_device):

    @editable
    ChestPlate : trigger_device = trigger_device{}

    @editable
    TreasureGranter : item_granter_device = item_granter_device{}

    OnBegin<override>()<suspends> : void =
        ChestPlate.TriggeredEvent.Subscribe(OnChestStep)

    OnChestStep(MaybeAgent : ?agent) : void =
        # All three guards in one flat if — comma means AND.
        # If any guard fails, the whole block is skipped (early return).
        if:
            A := MaybeAgent?           # Guard 1: real agent
            Character := A.GetFortCharacter[]  # Guard 2: has a character
            Character.IsActive[]       # Guard 3: still alive
        then:
            # Happy path — grant the shore treasure.
            TreasureGranter.GrantItem(A)

Pattern 2 — Early return from a value-returning helper

Sometimes you want to compute something (like a score bonus) and return early with a default value when conditions aren't met. Verse functions can return values, and you can use if/else as an expression.

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

# Clifftop bonus calculator: awards extra score for surviving players.
# Returns 0 if the player is eliminated, otherwise returns 100.
clifftop_bonus_device := class(creative_device):

    @editable
    BonusPlate : trigger_device = trigger_device{}

    @editable
    ScoreGranter : score_manager_device = score_manager_device{}

    OnBegin<override>()<suspends> : void =
        BonusPlate.TriggeredEvent.Subscribe(OnBonusStep)

    # Helper that returns a bonus amount — 0 is the "early return" default.
    GetBonus(A : agent) : int =
        # If the character is not active, the if fails and we fall to else.
        if (Character := A.GetFortCharacter[], Character.IsActive[]):
            100   # Alive — full clifftop bonus
        else:
            0     # Eliminated — no bonus (early-return equivalent)

    OnBonusStep(MaybeAgent : ?agent) : void =
        # Guard: unwrap the agent option first.
        if (A := MaybeAgent?):
            Bonus := GetBonus(A)
            # Only call the score manager if there is actually a bonus.
            if (Bonus > 0):
                ScoreGranter.SetScoreForPlayer(A, Bonus)

Pattern 3 — Guarding against repeated triggers with a flag

A common island bug: the reward fires multiple times because the plate can be stepped on again. An early return on a var logic flag prevents double-granting.

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

# Cove beacon: can only be activated once per round.
cove_beacon_device := class(creative_device):

    @editable
    BeaconPlate : trigger_device = trigger_device{}

    @editable
    BeaconGranter : item_granter_device = item_granter_device{}

    # Flag: has the beacon already been claimed?
    var BeaconClaimed : logic = false

    OnBegin<override>()<suspends> : void =
        BeaconPlate.TriggeredEvent.Subscribe(OnBeaconStep)

    OnBeaconStep(MaybeAgent : ?agent) : void =
        # GUARD 1 — early return if already claimed.
        # `not BeaconClaimed` fails (and exits) if BeaconClaimed is true.
        if (not BeaconClaimed):
            if (A := MaybeAgent?):
                if (Character := A.GetFortCharacter[], Character.IsActive[]):
                    # Mark claimed BEFORE granting to prevent race conditions.
                    set BeaconClaimed = true
                    BeaconGranter.GrantItem(A)

Gotchas

1. ?agent is NOT agent — always unwrap

Events like trigger_device.TriggeredEvent pass ?agent, not agent. If you write OnTriggered(A : agent) the handler signature won't match and the subscription will silently fail or not compile. Always declare (MaybeAgent : ?agent) and unwrap with if (A := MaybeAgent?).

2. Failable calls MUST live inside if

GetFortCharacter[] and IsActive[] use the <decides> effect — they can fail. You cannot call them outside an if expression. Trying to do so is a compile error. This is exactly why the early-return pattern pairs so naturally with Verse: every guard is just an if that exits on failure.

3. if with multiple guards uses , not and

In Verse the comma , inside an if block is the logical AND for failable/boolean expressions. Writing if (A and B) where B is a failable expression will not compile. Use:

if (A := MaybeAgent?, Character := A.GetFortCharacter[], Character.IsActive[]):

4. set keyword required for mutable variables

When updating a var field (like the BeaconClaimed flag in Pattern 3), you must use set BeaconClaimed = true. Writing BeaconClaimed = true is a compile error — Verse treats bare assignment as a new binding, not a mutation.

5. message parameters need a localizes wrapper

If you ever pass text to a device method that expects a message type (e.g. a notification device), you cannot pass a raw string literal. Declare a helper:

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

Then call NotificationDevice.Show(MyText("Beacon claimed!")). There is no StringToMessage function in Verse.

6. Early return does not exist as a keyword — use if/else structure

Unlike C++ or Python, Verse has no return statement mid-function. The early-return effect is achieved by putting the happy-path code inside the if block and leaving the else (or simply the end of the function) empty. This is idiomatic Verse and compiles cleanly.

Build your own lesson with early_return_pattern

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 →