Overview
When your island grows beyond a single Verse file, you'll naturally split logic into folders — a Dock folder for fishing mechanics, a Cove folder for swimming challenges, a Clifftop folder for the lookout mini-game. The moment one folder needs to reference a type or device declared in another, you hit cross-folder access.
In UEFN, every Verse content folder compiles into its own module. By default, definitions inside that module are internal — invisible outside it. To share a definition across folders you must explicitly widen its access specifier to <public>. Once it's public, any other Verse file in the project can using its module path and reference it directly.
The player_reference_device is the perfect vehicle for this pattern: it holds a single agent reference that any other device on the island can query or react to via AgentUpdatedEvent, ActivatedEvent, and IsReferenced. Pair it with score_manager_device and you have a complete cross-folder reward pipeline — register the player at the dock, award points from the clifftop.
Reach for this pattern when:
- A player registered in one mini-game zone needs to be recognised in another.
- You want a central "current VIP player" reference that multiple devices react to.
- You're splitting a large island into logical Verse modules and need controlled sharing.
API Reference
(API surface could not be resolved for this device.)
Walkthrough
Scenario: The Sunny Cove Treasure Hunt
A player dives into the cove and touches a treasure prop (trigger). A player_reference_device on the dock registers them as the active treasure-finder. Up on the clifftop, a separate Verse device listens to AgentUpdatedEvent on that same reference device and immediately awards bonus score via score_manager_device. The whole thing plays out under a cel-shaded noon sun with seagulls wheeling overhead.
# File: CoveTreasureManager.verse (lives in the /Dock/ content folder)
# This device sits on the dock and owns the player_reference_device.
# It registers whoever touches the cove treasure chest.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
cove_treasure_manager<public> := class(creative_device):
# The trigger placed on the treasure chest prop in the cove
@editable
TreasureTrigger : trigger_device = trigger_device{}
# The player_reference_device placed on the dock — holds the active finder
@editable
FinderReference : player_reference_device = player_reference_device{}
# Score manager that awards the initial discovery bonus
@editable
DiscoveryScorer : score_manager_device = score_manager_device{}
OnBegin<override>()<suspends> : void =
# Subscribe to the treasure trigger — fires when a player steps on it
TreasureTrigger.TriggeredEvent.Subscribe(OnTreasureFound)
# Also listen for when the reference device confirms a new agent is stored
FinderReference.AgentUpdatedEvent.Subscribe(OnFinderConfirmed)
# Called when a player touches the treasure chest trigger
OnTreasureFound(Agent : agent) : void =
# Register this player as the active treasure-finder
FinderReference.Register(Agent)
# Called once player_reference_device confirms the new agent is stored
OnFinderConfirmed(Agent : agent) : void =
# Award the discovery score to the confirmed finder
DiscoveryScorer.Activate(Agent)
# Enable the scorer so it can be triggered again if needed
DiscoveryScorer.Enable(Agent)
Line-by-line breakdown:
| Line | What it does |
|---|---|
@editable TreasureTrigger |
Wires the in-editor trigger placed on the cove chest prop. |
@editable FinderReference |
Wires the player_reference_device sitting on the dock. |
@editable DiscoveryScorer |
Wires the score manager that gives the discovery bonus. |
TreasureTrigger.TriggeredEvent.Subscribe(OnTreasureFound) |
Subscribes to the trigger's event — fires with the touching agent. |
FinderReference.Register(Agent) |
Stores the agent inside the reference device, broadcasting AgentUpdatedEvent. |
FinderReference.AgentUpdatedEvent.Subscribe(OnFinderConfirmed) |
Reacts to the reference device confirming the new agent. |
DiscoveryScorer.Activate(Agent) |
Awards the configured point value to the confirmed finder. |
Now the clifftop device, in a different folder, listens to the same player_reference_device:
# File: ClifftopLookout.verse (lives in the /Clifftop/ content folder)
# This device is placed on the clifftop and reacts to the dock's reference device.
# It awards a bonus when the registered finder reaches the clifftop trigger.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
clifftop_lookout<public> := class(creative_device):
# The SAME player_reference_device wired in the editor — cross-folder access
# via @editable: no module path needed, the editor resolves the reference.
@editable
FinderReference : player_reference_device = player_reference_device{}
# Trigger at the top of the cliff — player must reach it to claim bonus
@editable
ClifftopTrigger : trigger_device = trigger_device{}
# Score manager for the clifftop bonus
@editable
BonusScorer : score_manager_device = score_manager_device{}
OnBegin<override>()<suspends> : void =
ClifftopTrigger.TriggeredEvent.Subscribe(OnPlayerReachedClifftop)
OnPlayerReachedClifftop(Agent : agent) : void =
# Only award the bonus if this is the registered treasure-finder
if (FinderReference.IsReferenced[Agent]):
BonusScorer.Increment(Agent) # bump the award up by 1
BonusScorer.Activate(Agent) # then fire it
Key cross-folder insight: Both cove_treasure_manager and clifftop_lookout hold an @editable field typed player_reference_device. In the UEFN editor you drag the same placed device actor into both fields. The Verse compiler never needs to import one Verse file from another — the editor's property panel is the bridge. This is the canonical cross-folder access pattern for devices.
Common patterns
Pattern 1 — Clearing the reference when the round resets
When a new round starts, clear the stored agent so stale data doesn't persist.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
round_reset_manager<public> := class(creative_device):
@editable
FinderReference : player_reference_device = player_reference_device{}
@editable
DiscoveryScorer : score_manager_device = score_manager_device{}
@editable
RoundEndTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
RoundEndTrigger.TriggeredEvent.Subscribe(OnRoundEnd)
OnRoundEnd(Agent : agent) : void =
# Wipe the stored agent — next round starts fresh
FinderReference.Clear()
# Reset the scorer so its trigger count goes back to zero
DiscoveryScorer.Reset()
Pattern 2 — Reacting to a failed registration attempt
If the reference device already holds a different player and can't accept a new one, AgentUpdateFailsEvent fires. Use it to give feedback.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
registration_guard<public> := class(creative_device):
@editable
FinderReference : player_reference_device = player_reference_device{}
# A score manager configured to DEDUCT points (set to negative in editor)
@editable
PenaltyScorer : score_manager_device = score_manager_device{}
@editable
CoveTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
CoveTrigger.TriggeredEvent.Subscribe(OnAttemptRegister)
# Subscribe to the failure event — fires if the slot is already taken
FinderReference.AgentUpdateFailsEvent.Subscribe(OnRegistrationFailed)
OnAttemptRegister(Agent : agent) : void =
FinderReference.Register(Agent)
OnRegistrationFailed(Agent : agent) : void =
# The agent tried to register but the slot was occupied — apply penalty
PenaltyScorer.Activate(Agent)
Pattern 3 — Enabling and disabling the scorer per-agent
Some score manager settings are per-agent. Use the agent overloads to gate scoring to only the registered finder.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/UI }
dock_score_gate<public> := class(creative_device):
@editable
FinderReference : player_reference_device = player_reference_device{}
@editable
DockScorer : score_manager_device = score_manager_device{}
@editable
DockTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
# When a new finder is registered, enable scoring only for them
FinderReference.AgentUpdatedEvent.Subscribe(OnNewFinder)
DockTrigger.TriggeredEvent.Subscribe(OnDockReached)
OnNewFinder(Agent : agent) : void =
# Enable the scorer specifically for the new finder
DockScorer.Enable(Agent)
OnDockReached(Agent : agent) : void =
if (FinderReference.IsReferenced[Agent]):
# Increment first, then activate for a boosted payout
DockScorer.Increment(Agent)
DockScorer.Activate(Agent)
else:
# Not the registered finder — disable scoring for this agent
DockScorer.Disable(Agent)
Gotchas
1. @editable is the cross-folder bridge — not using
You do not write using { /MyProject/Dock/cove_treasure_manager } in the clifftop file to share a device reference. The Verse module system doesn't work that way for placed devices. Instead, both Verse classes expose the same player_reference_device as an @editable field and you drag the same actor into both slots in the UEFN Details panel. The editor resolves the shared reference at runtime.
2. IsReferenced is a deciding expression
FinderReference.IsReferenced[Agent] uses square brackets and can only appear inside an if (or another failable context). Writing FinderReference.IsReferenced(Agent) with parentheses will not compile.
3. AgentUpdatedEvent vs AgentReplacedEvent
AgentUpdatedEventfires whenever the stored agent changes (including the first registration).AgentReplacedEventfires only when an existing agent is replaced by a new one. If you only want to react to the very first registration, subscribe toAgentUpdatedEventand checkIsReferencedinside the handler.
4. score_manager_device.Reset() has two overloads
Calling DiscoveryScorer.Reset() (no argument) resets the device globally. Calling DiscoveryScorer.Reset(Agent) resets it only for that agent. Use the agent overload when you want per-player resets between rounds without affecting other players' state.
5. Enable/Disable are also overloaded
Both score_manager_device.Enable() and Enable(Agent:agent) exist. The agent overload gates the device for a specific player only — useful when you want the scorer active for the registered finder but inactive for everyone else on the island.
6. Forgetting <public> on shared classes
If you define a helper class in one folder and try to reference it from another, the compiler reports Unknown identifier. Add <public> to the class declaration: my_helper<public> := class(creative_device):. Without it the definition is internal to its module and invisible elsewhere.