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
@editablefields let you drag your three buttons and the HUD device onto this class in the Details panel. Without@editablefields you cannot call a placed device — a bareButton.Show()fails with Unknown identifier. ChestText<localizes>(S:string):messageis itself a helper — a text helper. Amessageparameter needs a localized value, and this one-liner wraps any string into one. There is noStringToMessage.RewardPlayer(Agent : agent) : voidis our star. It takes the presser and callsBanner.Show(Agent, ...). All the shared behavior lives here.OnPressA/B/Care event-handler methods. Each simply callsRewardPlayer(Agent). TheInteractedWithEventfor a button hands the handler a plainagent, so no unwrap is needed here.OnBeginsubscribes each button'sInteractedWithEventto 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:voidhelper can't contain a failable expression at its top level — put failable work behindif (...)or mark the helper<decides>when it can fail. - No int↔float auto-convert. If
ScoreForreturns anintbut a device wants afloat, you must convert explicitly. Verse never does it silently. - Messages are localized values. A helper that shows text must build a
messagevia a<localizes>helper. Passing a raw"string"toShowwill not compile, and there is noStringToMessage. - Helpers are methods — call them without a prefix inside the class. From within the class write
RewardPlayer(Agent), notSelf.RewardPlayer(...)and not through a device field. - Unwrap
?agentbefore use. Listenable events hand?agent; never pass the option straight intoShow. Unwrap withif (A := Maybe?):first — ideally inside a helper so you only write it once. @editableis mandatory for placed devices. A helper can use a device, but that device must still be a declared@editablefield on the class, wired in the Details panel.