Shared Helper Modules: Reusable Verse Logic Across Your Island
Every island grows messy fast — score logic here, timer resets there, cinematic triggers scattered everywhere. Verse modules let you pull that shared logic into one place and call it from any device on your island. In this article you'll build a sunny coastal cove where a dock-side challenge uses a shared helper module to coordinate a timer, a score manager, a cinematic sequence, and a teleporter — all from clean, reusable code.
shared_helper_module in ~90 seconds.Overview
A shared helper module is a Verse module block (or a folder whose name becomes a module) that holds functions, constants, and types you want to call from more than one creative_device on your island. Instead of copy-pasting the same "reset the timer and award points" logic into five different devices, you write it once in the module and import it everywhere.
When to reach for it:
- You have two or more devices that need to run the same sequence of API calls (e.g., start a timer, play a cinematic, then teleport the winner).
- You want to enforce consistent game rules — every scoring moment goes through the same function, so tweaking the formula is a one-line change.
- Your
OnBeginbodies are growing past ~30 lines and you want to name the intent clearly.
The sunny cove scenario: players dive off a clifftop dock, swim through a glowing underwater arch, and surface at a shore checkpoint. A pressure plate triggers the challenge. A shared module handles the "challenge started" and "challenge completed" logic — starting the countdown timer, playing the cinematic fanfare, awarding score, and teleporting the winner back to the dock — so both the start device and the finish device call the same helpers.
API Reference
(API surface could not be resolved for this device.)
Walkthrough
We'll build three files:
cove_helpers.verse— the shared module with two helper functions.cove_challenge_start.verse— the device placed at the clifftop dock that callsStartChallenge.cove_challenge_finish.verse— the device placed at the shore checkpoint that callsCompleteChallenge.
File 1 — cove_helpers.verse (the shared module)
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# Folder name = module name: cove_helpers
# Any device in the project can `using { cove_helpers }` to access these.
cove_helpers := module:
# Starts the cove challenge for an agent:
# resets + starts the timer, plays the intro cinematic.
StartChallenge(Agent : agent,
ChallengeTimer : timer_device,
IntroCinematic : cinematic_sequence_device) : void =
ChallengeTimer.Reset(Agent)
ChallengeTimer.Start(Agent)
IntroCinematic.Play(Agent)
# Completes the cove challenge for an agent:
# stops the timer, awards score, plays the win cinematic, teleports back to dock.
CompleteChallenge(Agent : agent,
ChallengeTimer : timer_device,
Scorer : score_manager_device,
WinCinematic : cinematic_sequence_device,
DockTeleporter : teleporter_device) : void =
ChallengeTimer.Reset(Agent)
Scorer.Activate(Agent)
WinCinematic.Play(Agent)
DockTeleporter.Teleport(Agent)
Line-by-line explanation
| Line | What it does |
|---|---|
cove_helpers := module: |
Declares the module. Because this file lives in a folder called cove_helpers, UEFN maps the folder to this module name automatically. |
StartChallenge(...) |
Plain Verse function — no class, no @editable. Takes live device references passed in by the caller. |
ChallengeTimer.Reset(Agent) |
Calls timer_device.Reset(Agent:agent) — resets the countdown for this specific player. |
ChallengeTimer.Start(Agent) |
Calls timer_device.Start(Agent:agent) — begins the countdown. |
IntroCinematic.Play(Agent) |
Calls cinematic_sequence_device.Play(Agent:agent) — plays the splash-entry sequence for that player. |
CompleteChallenge(...) |
Stops the timer, grants points, plays the win fanfare, teleports the player back to the dock. |
Scorer.Activate(Agent) |
Calls score_manager_device.Activate(Agent:agent) — awards the configured point value to the agent. |
DockTeleporter.Teleport(Agent) |
Calls teleporter_device.Teleport(Agent:agent) — sends the player directly to the dock teleporter's location. |
File 2 — cove_challenge_start.verse (clifftop dock device)
This creative_device is placed at the pressure plate on the dock. When a player steps on it, it calls the shared StartChallenge helper.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { cove_helpers }
cove_challenge_start := class(creative_device):
# Wire these in the UEFN Details panel
@editable
DockTimer : timer_device = timer_device{}
@editable
IntroCinematic : cinematic_sequence_device = cinematic_sequence_device{}
# Called once when the island starts
OnBegin<override>()<suspends> : void =
# The trigger_device / pressure plate fires an agent event;
# here we simulate that with a damage_volume at the dock edge.
DockVolume.AgentEntersEvent.Subscribe(OnPlayerEntersDock)
@editable
DockVolume : damage_volume_device = damage_volume_device{}
# Handler: player stepped onto the dock launch zone
OnPlayerEntersDock(Agent : agent) : void =
# Delegate all setup logic to the shared helper
StartChallenge(Agent, DockTimer, IntroCinematic)
File 3 — cove_challenge_finish.verse (shore checkpoint device)
Placed at the glowing arch at the shore. When a player surfaces through it, CompleteChallenge fires.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { cove_helpers }
cove_challenge_finish := class(creative_device):
@editable
DockTimer : timer_device = timer_device{}
@editable
Scorer : score_manager_device = score_manager_device{}
@editable
WinCinematic : cinematic_sequence_device = cinematic_sequence_device{}
@editable
DockTeleporter : teleporter_device = teleporter_device{}
@editable
FinishVolume : damage_volume_device = damage_volume_device{}
OnBegin<override>()<suspends> : void =
FinishVolume.AgentEntersEvent.Subscribe(OnPlayerReachesShore)
OnPlayerReachesShore(Agent : agent) : void =
CompleteChallenge(Agent, DockTimer, Scorer, WinCinematic, DockTeleporter)
Both devices share zero duplicated logic. Changing the scoring formula or swapping the cinematic means editing cove_helpers.verse once.
Common patterns
Pattern 1 — Disabling the finish zone until the challenge is active
Use damage_volume_device.Disable() to prevent players from cheating by running straight to the shore.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_gate_controller := class(creative_device):
@editable
StartVolume : damage_volume_device = damage_volume_device{}
@editable
FinishVolume : damage_volume_device = damage_volume_device{}
OnBegin<override>()<suspends> : void =
# Shore checkpoint is OFF until the challenge begins
FinishVolume.Disable()
StartVolume.AgentEntersEvent.Subscribe(OnChallengeBegins)
OnChallengeBegins(Agent : agent) : void =
# Now the finish zone is live for everyone
FinishVolume.Enable()
# Optionally re-disable after a window using timer SuccessEvent
Pattern 2 — Teleporting the player back to the dock on timer failure
If the countdown expires before the player reaches shore, teleport them back and reset.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_timeout_handler := class(creative_device):
@editable
ChallengeTimer : timer_device = timer_device{}
@editable
DockTeleporter : teleporter_device = teleporter_device{}
@editable
DockSpawner : player_spawner_device = player_spawner_device{}
OnBegin<override>()<suspends> : void =
ChallengeTimer.FailureEvent.Subscribe(OnTimerFailed)
# FailureEvent sends ?agent — must unwrap before use
OnTimerFailed(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
# Send them back to the dock start
DockTeleporter.Teleport(Agent)
ChallengeTimer.Reset(Agent)
Key point:
timer_device.FailureEventis alistenable(?agent)— the agent is optional. Always unwrap withif (Agent := MaybeAgent?):before calling anyagent-typed API.
Pattern 3 — Tracking the challenge winner with player_reference_device
Record which player completed the cove challenge fastest so a leaderboard hologram can display them.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_winner_tracker := class(creative_device):
@editable
FinishVolume : damage_volume_device = damage_volume_device{}
@editable
WinnerReference : player_reference_device = player_reference_device{}
@editable
Scorer : score_manager_device = score_manager_device{}
OnBegin<override>()<suspends> : void =
FinishVolume.AgentEntersEvent.Subscribe(OnPlayerFinished)
# React whenever the tracked winner changes
WinnerReference.AgentUpdatedEvent.Subscribe(OnWinnerUpdated)
OnPlayerFinished(Agent : agent) : void =
# Register this agent as the current winner
WinnerReference.Register(Agent)
# Award score
Scorer.Activate(Agent)
OnWinnerUpdated(NewWinner : agent) : void =
# The hologram on player_reference_device now shows the new winner automatically
# You could also play a fanfare here
WinnerReference.Activate()
Gotchas
1. Module functions receive device references — they don't own @editable fields
A plain module function cannot declare @editable fields. Those belong to creative_device classes. Pass your placed devices as parameters to module functions, as shown in the walkthrough. Trying to put @editable inside a module block is a compile error.
2. timer_device.FailureEvent and SuccessEvent send ?agent, not agent
Both events are listenable(?agent). Your handler signature must be (MaybeAgent : ?agent) and you must unwrap: if (Agent := MaybeAgent?):. Forgetting the ? causes a type mismatch compile error.
3. cinematic_sequence_device.Play(Agent) requires the device set to a specific player mode
Play() (no argument) works when the device is set to Everyone. Play(Agent:agent) requires the device to be set to a mode other than Everyone in the Details panel. Mismatching these silently does nothing at runtime.
4. score_manager_device.Activate(Agent) respects the Activating Team setting
If you set Activating Team to a specific team in the Details panel, only agents on that team will have their score updated. If you're calling it from a shared helper and scores aren't registering, check the device's team filter first.
5. Module using imports must be in every file that references the module's symbols
Adding using { cove_helpers } in cove_challenge_start.verse does NOT make it available in cove_challenge_finish.verse. Each file needs its own using directive.
6. damage_volume_device.SetDamage clamps between 1 and 500
If you repurpose a damage_volume_device purely as a trigger zone (no actual damage), set its Damage Per Second to 1 in the Details panel and rely on AgentEntersEvent for logic — or set it to 0 damage in the panel (the SetDamage API clamps to 1 minimum, but the panel allows 0).
7. No int↔float auto-conversion
If you pass a score delta as an int to a function expecting float (or vice versa), Verse will not coerce it. Use explicit casts: Float(MyInt) or Int[MyFloat] (the latter is failable).
Build your own lesson with shared_helper_module
Generate a personalized, compile-checked lesson grounded in this exact reference.
Build a lesson →