Reference Verse compiles

Functions as Helpers: Clean Verse Logic on a Sunlit Dock

Every great island has moments that need more than one line of code — a pirate dock where a pressure plate unlocks a treasure chest, flashes a HUD message, and resets itself after a delay. Instead of cramming all that logic into one giant `OnBegin`, you write small, focused *helper functions* that each do one job. This article shows you exactly how to split trigger and HUD device calls across helpers so your Verse stays readable, reusable, and easy to debug.

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

Overview

A function-as-helper isn't a placed device at all — it's a plain Verse function you declare inside your class(creative_device) and call from your event handlers to avoid repeating yourself. This is the single most important habit for keeping island code readable.

Picture a sunny cove with a wooden dock. Three chest buttons sit along the shore. When a player presses ANY of them you want to: award points, flash a message, and play a sound. Without helpers you'd paste that block three times. With a helper you write RewardPlayer(Agent) once and call it from all three handlers. Change the reward later? One edit, not three.

Reach for a helper function whenever:

  • Two or more event handlers do the same work.
  • A handler is getting long and you want to name a step (GrantLoot, ShowHint).
  • You need a small pure calculation (like ScoreFor(Tier)) reused in several places.

Because a helper is just Verse, it can be <transacts>, <decides>, <suspends> — matching the effects of what it does. That's the whole API surface: the Verse function itself.

API Reference

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

Walkthrough

Our scene: a sunny cove with three chest button_devices and one hud_message_device. Every button should reward the presser. We write ONE helper, RewardPlayer, and wire all three buttons to handlers that call it.

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

cove_treasure_manager := class(creative_device):

    @editable
    ChestButtonA : button_device = button_device{}

    @editable
    ChestButtonB : button_device = button_device{}

    @editable
    ChestButtonC : button_device = button_device{}

    @editable
    Banner : hud_message_device = hud_message_device{}

    # A localized-text helper so we can pass a message value, not a raw string.
    ChestText<localizes>(S : string) : message = "{S}"

    # THE HELPER: one place that defines "what pressing a chest does".
    RewardPlayer(Agent : agent) : void =
        Banner.Show(Agent, ChestText("Treasure claimed! +100"))

    # Three tiny handlers, each just forwarding to the shared helper.
    OnPressA(Agent : agent) : void =
        RewardPlayer(Agent)

    OnPressB(Agent : agent) : void =
        RewardPlayer(Agent)

    OnPressC(Agent : agent) : void =
        RewardPlayer(Agent)

    OnBegin<override>()<suspends>:void =
        ChestButtonA.InteractedWithEvent.Subscribe(OnPressA)
        ChestButtonB.InteractedWithEvent.Subscribe(OnPressB)
        ChestButtonC.InteractedWithEvent.Subscribe(OnPressC)

Line by line:

  • The four @editable fields let you drag your three buttons and the HUD device onto this class in the Details panel. Without @editable fields you cannot call a placed device — a bare Button.Show() fails with Unknown identifier.
  • ChestText<localizes>(S:string):message is itself a helper — a text helper. A message parameter needs a localized value, and this one-liner wraps any string into one. There is no StringToMessage.
  • RewardPlayer(Agent : agent) : void is our star. It takes the presser and calls Banner.Show(Agent, ...). All the shared behavior lives here.
  • OnPressA/B/C are event-handler methods. Each simply calls RewardPlayer(Agent). The InteractedWithEvent for a button hands the handler a plain agent, so no unwrap is needed here.
  • OnBegin subscribes each button's InteractedWithEvent to its handler. This runs once when the game starts.

Want every chest to reward 200 instead? Edit one line inside RewardPlayer. That is the payoff of function-as-helper.

Common patterns

Pattern 1 — A helper that RETURNS a value (<decides> calculation)

Helpers don't just do things; they can compute answers. Here a helper decides how many points a chest tier is worth, keeping the math in one named place.

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

score_helper_device := class(creative_device):

    @editable
    BronzeButton : button_device = button_device{}

    @editable
    Scoreboard : hud_message_device = hud_message_device{}

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

    # Pure calculation helper: given a tier name, return its point value.
    ScoreFor(Tier : string) : int =
        if (Tier = "gold"). "") {}
        case (Tier):
            "gold" => 300
            "silver" => 200
            _ => 100

    OnBronze(Agent : agent) : void =
        Points := ScoreFor("bronze")
        Scoreboard.Show(Agent, Msg("You earned {Points} points"))

    OnBegin<override>()<suspends>:void =
        BronzeButton.InteractedWithEvent.Subscribe(OnBronze)

The ScoreFor helper centralizes the tier→points rule. Any handler that needs a score value calls it instead of hard-coding numbers.

Pattern 2 — A helper that takes the DEVICE as a parameter

Make a helper generic by passing the device to act on. One LightUp helper can control any button's affordances.

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

dock_lights_device := class(creative_device):

    @editable
    LeftButton : button_device = button_device{}

    @editable
    RightButton : button_device = button_device{}

    # Helper reused for BOTH buttons — pass the one to disable.
    LockButton(Which : button_device, Agent : agent) : void =
        Which.SetInteractable(Agent, false)

    OnLeft(Agent : agent) : void =
        LockButton(LeftButton, Agent)

    OnRight(Agent : agent) : void =
        LockButton(RightButton, Agent)

    OnBegin<override>()<suspends>:void =
        LeftButton.InteractedWithEvent.Subscribe(OnLeft)
        RightButton.InteractedWithEvent.Subscribe(OnRight)

Because LockButton takes a button_device argument, one helper serves both buttons — press one and that same button locks itself.

Pattern 3 — A helper that unwraps an optional agent from a listenable

Many events hand you a ?agent. Wrap the unwrap-then-act in a helper so every subscriber stays tidy.

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

cove_greeter_device := class(creative_device):

    @editable
    WelcomeMat : trigger_device = trigger_device{}

    @editable
    Banner : hud_message_device = hud_message_device{}

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

    # Helper: safely turn a ?agent into an action, or do nothing.
    GreetIfPresent(MaybeAgent : ?agent) : void =
        if (Agent := MaybeAgent?):
            Banner.Show(Agent, Hello("Welcome to the cove!"))

    OnStepped(MaybeAgent : ?agent) : void =
        GreetIfPresent(MaybeAgent)

    OnBegin<override>()<suspends>:void =
        WelcomeMat.TriggeredEvent.Subscribe(OnStepped)

TriggeredEvent sends a ?agent. The GreetIfPresent helper does the if (Agent := MaybeAgent?) unwrap once, so the handler reads cleanly.

Gotchas

  • Effects must match. If your helper calls a <decides> operation (like array lookup) or fails, the helper's effect specifiers must allow it. A plain :void helper can't contain a failable expression at its top level — put failable work behind if (...) or mark the helper <decides> when it can fail.
  • No int↔float auto-convert. If ScoreFor returns an int but a device wants a float, you must convert explicitly. Verse never does it silently.
  • Messages are localized values. A helper that shows text must build a message via a <localizes> helper. Passing a raw "string" to Show will not compile, and there is no StringToMessage.
  • Helpers are methods — call them without a prefix inside the class. From within the class write RewardPlayer(Agent), not Self.RewardPlayer(...) and not through a device field.
  • Unwrap ?agent before use. Listenable events hand ?agent; never pass the option straight into Show. Unwrap with if (A := Maybe?): first — ideally inside a helper so you only write it once.
  • @editable is mandatory for placed devices. A helper can use a device, but that device must still be a declared @editable field on the class, wired in the Details panel.

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 →