Overview
The crowd_volume_device spawns an animated NPC crowd inside a defined area of your island. By default the crowd cheers and reacts to nearby players, adding atmosphere to arenas, race tracks, concert stages, or any competitive space.
From Verse you get two direct controls:
Enable()— activates the crowd so it appears and cheers.Disable()— hides the crowd entirely.
Because the device has no built-in entry/exit events of its own, you pair it with a volume_device to detect when players enter or leave the crowd zone. The volume_device provides AgentEntersEvent, AgentExitsEvent, GetAgentsInVolume(), and IsInVolume() — giving you full situational awareness of who is standing in the crowd area.
Reach for this device when you want to:
- Reveal a cheering crowd only after a player crosses a finish line.
- Silence the crowd when the arena empties between rounds.
- Gate crowd activation on how many players are currently inside the zone.
API Reference
crowd_volume_device
Spawns a crowd to cheer you on.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
crowd_volume_device<public> := class<concrete><final>(creative_device_base):
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
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: An arena has a crowd zone behind the starting gate. The crowd is silent at the start. When the first player steps into the arena volume, the crowd erupts. When the last player leaves, the crowd goes quiet again. A periodic check also prints how many players are currently in the zone.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Localized helper so we can pass strings as `message` parameters if needed.
ArenaMessage<localizes>(S : string) : message = "{S}"
arena_crowd_manager := class(creative_device):
# The crowd volume device placed in the editor — the NPC audience.
@editable
CrowdVolume : crowd_volume_device = crowd_volume_device{}
# A volume_device placed over the same area — detects player presence.
@editable
ArenaVolume : volume_device = volume_device{}
# Track how many players are currently inside.
var PlayersInside : int = 0
OnBegin<override>()<suspends> : void =
# Start with the crowd disabled — silence before the action begins.
CrowdVolume.Disable()
# Subscribe to entry and exit events on the volume_device.
ArenaVolume.AgentEntersEvent.Subscribe(OnPlayerEntered)
ArenaVolume.AgentExitsEvent.Subscribe(OnPlayerExited)
# Continuously monitor crowd zone population every 5 seconds.
loop:
Sleep(5.0)
CheckArenaPopulation()
# Called whenever an agent enters the arena volume.
OnPlayerEntered(Agent : agent) : void =
set PlayersInside = PlayersInside + 1
if (PlayersInside = 1):
# First player arrived — wake the crowd!
CrowdVolume.Enable()
# Called whenever an agent exits the arena volume.
OnPlayerExited(Agent : agent) : void =
if (PlayersInside > 0):
set PlayersInside = PlayersInside - 1
if (PlayersInside = 0):
# Arena is empty — silence the crowd.
CrowdVolume.Disable()
# Periodic ground-truth check using GetAgentsInVolume.
CheckArenaPopulation() : void =
Agents := ArenaVolume.GetAgentsInVolume()
# Sync our counter with reality in case events were missed.
set PlayersInside = Agents.Length
if (PlayersInside = 0):
CrowdVolume.Disable()
else:
CrowdVolume.Enable()
Line-by-line explanation:
| Lines | What's happening |
|---|---|
@editable CrowdVolume |
References the placed crowd_volume_device — required to call Enable/Disable. |
@editable ArenaVolume |
References the placed volume_device — provides all entry/exit events and query methods. |
CrowdVolume.Disable() in OnBegin |
Ensures the crowd is hidden at game start. |
AgentEntersEvent.Subscribe(OnPlayerEntered) |
Wires the entry event to our handler. |
AgentExitsEvent.Subscribe(OnPlayerExited) |
Wires the exit event to our handler. |
loop + Sleep(5.0) |
Runs CheckArenaPopulation every 5 seconds as a safety net. |
OnPlayerEntered |
Increments the counter; enables the crowd when the first player arrives. |
OnPlayerExited |
Decrements the counter; disables the crowd when the last player leaves. |
GetAgentsInVolume() |
Returns the live array of agents — used to reconcile the counter with ground truth. |
Common patterns
Pattern 1 — Spotlight check: is a specific player in the crowd zone?
Use IsInVolume (a failable expression) to check whether a particular agent is standing in the zone right now.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
crowd_spotlight_check := class(creative_device):
@editable
CrowdVolume : crowd_volume_device = crowd_volume_device{}
@editable
ArenaVolume : volume_device = volume_device{}
# A button players press to check if they are "in the spotlight".
@editable
CheckButton : button_device = button_device{}
OnBegin<override>()<suspends> : void =
CrowdVolume.Enable()
CheckButton.InteractedWithEvent.Subscribe(OnButtonPressed)
# Keep the coroutine alive.
loop:
Sleep(9999.0)
OnButtonPressed(Agent : agent) : void =
# IsInVolume<decides> — wrap in `if` to handle the failable call.
if (ArenaVolume.IsInVolume[Agent]):
# Player IS in the crowd zone — crowd reacts.
CrowdVolume.Disable()
# Brief pause, then re-enable for a "wave" effect.
# (Spawn a concurrent task so we don't block the handler.)
spawn { CrowdWaveEffect() }
CrowdWaveEffect()<suspends> : void =
Sleep(1.5)
CrowdVolume.Enable()
Key point: IsInVolume has the <decides> effect — it is a failable expression and must be called inside an if (or another failure context). Calling it bare will not compile.
Pattern 2 — Round-based crowd toggle (Enable/Disable on a timer)
Simple round structure: crowd cheers during the round, goes quiet during the break.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
round_crowd_controller := class(creative_device):
@editable
CrowdVolume : crowd_volume_device = crowd_volume_device{}
# Duration of each active round in seconds.
@editable
RoundDuration : float = 60.0
# Duration of the break between rounds in seconds.
@editable
BreakDuration : float = 15.0
OnBegin<override>()<suspends> : void =
# Crowd starts disabled — silence before round 1.
CrowdVolume.Disable()
Sleep(3.0) # Brief countdown pause.
loop:
# --- Active round: crowd cheers ---
CrowdVolume.Enable()
Sleep(RoundDuration)
# --- Break: crowd goes quiet ---
CrowdVolume.Disable()
Sleep(BreakDuration)
Key point: Enable() and Disable() are plain void calls with no effects — safe to call anywhere, including inside a loop.
Pattern 3 — React to props entering the crowd zone
The volume_device also fires PropEnterEvent when a creative_prop enters the volume — useful for physics objects or moving set pieces rolling into the crowd area.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
crowd_prop_reactor := class(creative_device):
@editable
CrowdVolume : crowd_volume_device = crowd_volume_device{}
@editable
ArenaVolume : volume_device = volume_device{}
OnBegin<override>()<suspends> : void =
CrowdVolume.Disable()
ArenaVolume.PropEnterEvent.Subscribe(OnPropEntered)
ArenaVolume.PropExitEvent.Subscribe(OnPropExited)
loop:
Sleep(9999.0)
# A prop (e.g. a rolling boulder) entered the crowd zone — crowd reacts!
OnPropEntered(Prop : creative_prop) : void =
CrowdVolume.Enable()
# Prop left — crowd calms down.
OnPropExited(Prop : creative_prop) : void =
CrowdVolume.Disable()
Key point: PropEnterEvent and PropExitEvent hand the handler a creative_prop value directly — no unwrapping needed (unlike ?agent optionals on some other devices).
Gotchas
1. crowd_volume_device has NO entry/exit events — always pair with volume_device
The crowd_volume_device only exposes Enable() and Disable(). It cannot tell you when players enter or leave. You must place a separate volume_device over the same area and subscribe to its events for any presence-based logic.
2. IsInVolume is failable — always call it inside if
IsInVolume carries the <decides> effect, meaning it can fail (when the agent is NOT in the volume). You must call it inside a failure context:
# CORRECT
if (ArenaVolume.IsInVolume[Agent]):
CrowdVolume.Enable()
# WRONG — will not compile
ArenaVolume.IsInVolume(Agent) # missing failure context
Note the bracket syntax IsInVolume[Agent] when used inside if — this is the standard Verse failable-call pattern.
3. AgentEntersEvent / AgentExitsEvent hand a plain agent, not ?agent
Unlike some other devices that signal ?agent (optional), volume_device events deliver a concrete agent. No if (A := Agent?) unwrap is needed in your handler signature.
4. Enable() and Disable() are idempotent but not stateful in Verse
Calling Enable() on an already-enabled crowd (or Disable() on an already-disabled one) is safe and will not throw. However, Verse has no built-in way to query the current enabled state of the device — track it yourself with a var boolean if your logic needs to know.
5. Keep your coroutine alive
If your OnBegin body returns (falls off the end), all subscriptions are still active but any loop-based logic stops. If you only subscribe to events and do nothing else, add loop: Sleep(9999.0) at the end of OnBegin to keep the device coroutine running indefinitely.
6. GetAgentsInVolume() returns a snapshot, not a live collection
The array returned by GetAgentsInVolume() is a point-in-time snapshot. Cache it to a local variable and iterate — do not assume it stays up to date as players move.