Overview
The Rift Point Volume device (rift_point_volume_device) pairs with the in-game Rift Point item to create a complete plant-and-defuse loop. Drop the device in your map to mark a "bomb site"; players who hold the Rift Point can walk into the volume and begin planting. Your Verse code subscribes to the device's rich event set — PlantStartEvent, PlantEvent, DefuseEvent, DetonateEvent, and more — to drive score changes, UI updates, spawner toggles, or any other game logic.
Beyond the bomb-cycle events, the device also exposes volume occupancy tools: OnAgentEntered / OnAgentExited events and the GetAgentsInVolume() / IsInVolume() methods let you query who is standing in the zone at any moment, enabling defender-count HUDs, area-denial bonuses, or "hold the site" scoring.
When to reach for it:
- Search-and-Destroy / bomb-defuse game modes
- Objective zones where a carried item must be activated
- Any mechanic that needs to know who is currently inside a region and what stage of an interaction they are in
API Reference
rift_point_volume_device
The Rift Point volume is used to interface with and manage the Rift Point item, and provides an area that enables players to plant the item to create search and destroy style gameplay.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
rift_point_volume_device<public> := class<concrete><final>(creative_device_base, enableable):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
PlantStartEvent |
PlantStartEvent<public>:listenable(agent) |
Sends an event when planting the Rift Point is started, passing in the planting agent. |
PlantCancelEvent |
PlantCancelEvent<public>:listenable(agent) |
Sends an event when planting the Rift Point is canceled, passing in the planting agent. |
PlantEvent |
PlantEvent<public>:listenable(agent) |
Sends an event when the Rift Point is planted, passing in the planting agent. |
DefuseStartEvent |
DefuseStartEvent<public>:listenable(agent) |
Sends an event when defusing the Rift Point is started, passing in the defusing agent. |
DefuseCancelEvent |
DefuseCancelEvent<public>:listenable(agent) |
Sends an event when defusing the Rift Point is canceled, passing in the defusing agent. |
DefuseEvent |
DefuseEvent<public>:listenable(agent) |
Sends an event when the Rift Point is defused, passing in the defusing agent. |
DetonateEvent |
DetonateEvent<public>:listenable(agent) |
Sends an event when the Rift Point detonates, passing in the planting agent. |
OnAgentEntered |
OnAgentEntered<public>:listenable(agent) |
Sends an event when an agent enters the volume. |
OnAgentExited |
OnAgentExited<public>:listenable(agent) |
Sends an event when an agent exits the volume. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
IsInVolume |
IsInVolume<public>(Agent:agent)<transacts><decides>:void |
Is true when Agent is in the volume. |
GetAgentsInVolume |
GetAgentsInVolume<public>()<reads>:[]agent |
Returns an array of agents that are currently occupying the volume. |
volume_device
Used to track when agents enter and exit a volume.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
volume_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
AgentEntersEvent |
AgentEntersEvent<public>:listenable(agent) |
Signaled when an agent enters the device volume. |
AgentExitsEvent |
AgentExitsEvent<public>:listenable(agent) |
Signaled when an agent exits the device volume. |
PropEnterEvent |
PropEnterEvent<public>:listenable(creative_prop) |
Signaled when an creative_prop entered the device volume. |
PropExitEvent |
PropExitEvent<public>:listenable(creative_prop) |
Signaled when an creative_prop exited the device volume. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
GetAgentsInVolume |
GetAgentsInVolume<public>()<reads>:[]agent |
Returns an array of agents that are currently occupying the volume. |
IsInVolume |
IsInVolume<public>(Agent:agent)<transacts><decides>:void |
Succeeds when Agent is in the volume. |
Walkthrough
Scenario: A Two-Site Bomb Map
You have two Rift Point Volume devices placed on the map (Site A and Site B). When the bomb is planted, the defending team gets a visual cue (we toggle a cinematic_sequence_device). When it detonates, the attacking team scores a point via a score_manager_device. If a defender defuses it, the defending team scores instead. We also log how many players are holding the site when planting begins.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Attach this Verse device to your island.
# Wire up the two rift_point_volume_device references and the
# score_manager_device references in the Details panel.
bomb_site_manager_device := class(creative_device):
# Site A rift point volume — set in Details panel
@editable
SiteA : rift_point_volume_device = rift_point_volume_device{}
# Site B rift point volume — set in Details panel
@editable
SiteB : rift_point_volume_device = rift_point_volume_device{}
# Score manager for the attacking team (planters)
@editable
AttackScore : score_manager_device = score_manager_device{}
# Score manager for the defending team (defusers)
@editable
DefendScore : score_manager_device = score_manager_device{}
# Runs at game start — subscribe to all relevant events on both sites
OnBegin<override>()<suspends> : void =
# --- Site A subscriptions ---
SiteA.PlantStartEvent.Subscribe(OnPlantStart)
SiteA.PlantCancelEvent.Subscribe(OnPlantCancel)
SiteA.PlantEvent.Subscribe(OnPlanted)
SiteA.DefuseStartEvent.Subscribe(OnDefuseStart)
SiteA.DefuseCancelEvent.Subscribe(OnDefuseCancel)
SiteA.DefuseEvent.Subscribe(OnDefused)
SiteA.DetonateEvent.Subscribe(OnDetonated)
SiteA.OnAgentEntered.Subscribe(OnSiteEntered)
SiteA.OnAgentExited.Subscribe(OnSiteExited)
# --- Site B subscriptions (same handlers — they work for both sites) ---
SiteB.PlantStartEvent.Subscribe(OnPlantStart)
SiteB.PlantCancelEvent.Subscribe(OnPlantCancel)
SiteB.PlantEvent.Subscribe(OnPlanted)
SiteB.DefuseStartEvent.Subscribe(OnDefuseStart)
SiteB.DefuseCancelEvent.Subscribe(OnDefuseCancel)
SiteB.DefuseEvent.Subscribe(OnDefused)
SiteB.DetonateEvent.Subscribe(OnDetonated)
SiteB.OnAgentEntered.Subscribe(OnSiteEntered)
SiteB.OnAgentExited.Subscribe(OnSiteExited)
# -----------------------------------------------------------------------
# Plant cycle handlers
# -----------------------------------------------------------------------
# Called when a player begins the planting animation
OnPlantStart(Planter : agent) : void =
# Count how many players are already on the site when planting starts.
# We check both sites — whichever one the planter is in.
AgentsAtSiteA := SiteA.GetAgentsInVolume()
AgentsAtSiteB := SiteB.GetAgentsInVolume()
# Use the larger count (the active site)
ActiveCount := if (AgentsAtSiteA.Length > AgentsAtSiteB.Length):
AgentsAtSiteA.Length
else:
AgentsAtSiteB.Length
Print("Planting started — {ActiveCount} player(s) on site")
# Called when the player cancels the plant (moved away / interrupted)
OnPlantCancel(Planter : agent) : void =
Print("Plant canceled")
# Called when the Rift Point is fully planted
OnPlanted(Planter : agent) : void =
Print("Rift Point planted! Defenders must defuse.")
# You could enable a countdown timer device here
# -----------------------------------------------------------------------
# Defuse cycle handlers
# -----------------------------------------------------------------------
OnDefuseStart(Defuser : agent) : void =
Print("Defuse attempt started")
OnDefuseCancel(Defuser : agent) : void =
Print("Defuse canceled — keep pressure on!")
# Defenders successfully defused — award defending team a point
OnDefused(Defuser : agent) : void =
DefendScore.Activate(Defuser)
Print("Rift Point defused! Defenders score.")
# -----------------------------------------------------------------------
# Detonation handler
# -----------------------------------------------------------------------
# Rift Point detonated — award attacking team a point
OnDetonated(Planter : agent) : void =
AttackScore.Activate(Planter)
Print("BOOM! Rift Point detonated. Attackers score.")
# -----------------------------------------------------------------------
# Volume occupancy handlers
# -----------------------------------------------------------------------
OnSiteEntered(Agent : agent) : void =
Print("Agent entered bomb site")
OnSiteExited(Agent : agent) : void =
Print("Agent exited bomb site")
Line-by-line highlights
| Lines | What's happening |
|---|---|
@editable fields |
Lets you wire up the two site devices and both score managers in the UEFN Details panel — no hard-coding. |
OnBegin subscriptions |
All nine events on each site are subscribed in one place. Handlers are shared across both sites because the game logic is identical. |
GetAgentsInVolume() in OnPlantStart |
Queries both volumes at the moment planting begins to find out how contested the site is — useful for "bonus points for planting under pressure" mechanics. |
DefendScore.Activate(Defuser) |
Calls a real device method to award the point; no Print-only logic. |
AttackScore.Activate(Planter) |
The DetonateEvent passes the planting agent, so the original bomber gets credit even if they died before detonation. |
Common patterns
Pattern 1 — Check if a specific player is on site before allowing a round to end
Use IsInVolume() as a failable expression inside an if to gate logic on whether a particular agent is still standing in the zone.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Prevents the round-end trigger from firing while any player
# is still physically inside the bomb site volume.
site_guard_device := class(creative_device):
@editable
BombSite : rift_point_volume_device = rift_point_volume_device{}
# A trigger_device wired to "End Round" — we check the site before allowing it
@editable
RoundEndTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
RoundEndTrigger.TriggeredEvent.Subscribe(OnRoundEndAttempt)
OnRoundEndAttempt(MaybeAgent : ?agent) : void =
# GetAgentsInVolume returns everyone currently inside
Occupants := BombSite.GetAgentsInVolume()
if (Occupants.Length = 0):
Print("Site is clear — round can end")
# Enable your end-round device here
else:
Print("Site still occupied by {Occupants.Length} player(s) — round continues")
# Demonstrate IsInVolume: check a specific agent
IsPlayerOnSite(Agent : agent) : logic =
if (BombSite.IsInVolume[Agent]):
return true
return false
Pattern 2 — React to agents entering and exiting the volume for an area-control HUD
Subscribe to OnAgentEntered and OnAgentExited to maintain a live count of defenders holding the site, and use GetAgentsInVolume() to verify the count on exit.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Tracks how many players are holding the bomb site at any moment.
# Pair with a HUD message device to show "Defenders on site: N".
site_occupancy_tracker_device := class(creative_device):
@editable
BombSite : rift_point_volume_device = rift_point_volume_device{}
# A hud_message_device wired to show occupancy count
@editable
OccupancyHUD : hud_message_device = hud_message_device{}
OnBegin<override>()<suspends> : void =
BombSite.OnAgentEntered.Subscribe(OnEntered)
BombSite.OnAgentExited.Subscribe(OnExited)
OnEntered(Agent : agent) : void =
# Re-query the live list after entry
Current := BombSite.GetAgentsInVolume()
Print("Player entered site. Now {Current.Length} on site.")
# You could call OccupancyHUD.Show() or update a tracker device here
OnExited(Agent : agent) : void =
# Re-query after exit to get the accurate remaining count
Current := BombSite.GetAgentsInVolume()
Print("Player left site. Now {Current.Length} remaining.")
Pattern 3 — Full defuse-cycle state machine with cancel tracking
Track whether a defuse is currently in progress so you can award a bonus to defenders who complete a defuse after a cancel.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Awards a "clutch defuse" bonus when a defender completes a defuse
# that was previously interrupted at least once.
clutch_defuse_device := class(creative_device):
@editable
BombSite : rift_point_volume_device = rift_point_volume_device{}
@editable
BonusGranter : item_granter_device = item_granter_device{}
# Track whether this defuse attempt was preceded by a cancel
var WasCanceled : logic = false
var DefuseInProgress : logic = false
OnBegin<override>()<suspends> : void =
BombSite.PlantEvent.Subscribe(OnBombPlanted)
BombSite.DefuseStartEvent.Subscribe(OnDefuseStarted)
BombSite.DefuseCancelEvent.Subscribe(OnDefuseCanceled)
BombSite.DefuseEvent.Subscribe(OnDefuseComplete)
BombSite.DetonateEvent.Subscribe(OnBombDetonated)
OnBombPlanted(Planter : agent) : void =
# Reset state for the new plant
set WasCanceled = false
set DefuseInProgress = false
Print("Bomb planted — defuse window open")
OnDefuseStarted(Defuser : agent) : void =
set DefuseInProgress = true
Print("Defuse started")
OnDefuseCanceled(Defuser : agent) : void =
set DefuseInProgress = false
set WasCanceled = true
Print("Defuse interrupted — clutch opportunity active")
OnDefuseComplete(Defuser : agent) : void =
set DefuseInProgress = false
if (WasCanceled?):
# Grant a bonus item for clutching the defuse
BonusGranter.GrantItem(Defuser)
Print("CLUTCH DEFUSE — bonus granted!")
else:
Print("Clean defuse")
set WasCanceled = false
OnBombDetonated(Planter : agent) : void =
set DefuseInProgress = false
set WasCanceled = false
Print("Bomb detonated")
Gotchas
1. IsInVolume is a failable expression — use it inside if
IsInVolume is declared <decides>, meaning it fails (rather than returning false) when the agent is not in the volume. You must call it inside an if or another failure-context:
# CORRECT
if (BombSite.IsInVolume[Agent]):
Print("On site")
# WRONG — IsInVolume is not a bool-returning function
# let Result = BombSite.IsInVolume(Agent) -- compile error
2. DetonateEvent passes the planting agent, not a defuser
When the Rift Point explodes, the event payload is the agent who planted it. If that player has been eliminated, the agent reference is still valid for scoring purposes, but don't assume the agent is alive.
3. GetAgentsInVolume() is a snapshot, not a live binding
The array returned by GetAgentsInVolume() is a point-in-time snapshot. Cache it in a local variable and use it immediately; don't store it across suspension points expecting it to stay current.
4. Event handlers must be methods, not lambdas, when subscribing
All Subscribe calls require a method reference at class scope. Inline lambdas are not supported for listenable subscriptions in UEFN Verse:
# CORRECT
BombSite.PlantEvent.Subscribe(OnPlanted)
# WRONG
# BombSite.PlantEvent.Subscribe(fun(A:agent):void = Print("planted")) -- not supported
5. OnAgentEntered / OnAgentExited vs. volume_device events
The rift_point_volume_device exposes OnAgentEntered and OnAgentExited directly on itself — you do not need a separate volume_device to track occupancy. Using a separate volume_device alongside the rift point volume is fine for prop tracking (PropEnterEvent/PropExitEvent), but for agent occupancy, always use the rift point volume's own events to avoid double-counting or mismatched volumes.
6. @editable fields are required to reference placed devices
You cannot call rift_point_volume_device methods on a bare identifier. Every device reference must be declared as an @editable field inside your class(creative_device) and wired up in the UEFN Details panel. A top-level Device.PlantEvent.Subscribe(...) with no class context will fail to compile with Unknown identifier.