Reference Devices compiles

rift_point_volume_device: Build Search-and-Destroy Gameplay

The `rift_point_volume_device` is the engine behind Search-and-Destroy–style gameplay in UEFN — it defines the zone where players plant the Rift Point item, tracks every stage of the plant/defuse cycle, and lets your Verse code react to detonations, cancellations, and volume occupancy in real time. If you want a bomb site, an objective zone, or any mechanic where a player carries an item to a location and activates it, this is your device.

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

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.

Build your own lesson with rift_point_volume_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 →