Overview
The damage_volume_device is an invisible box you place in your level that applies damage to any agent, vehicle, or creature inside it — once per tick. It solves a very common game problem: "I need an area that hurts whoever stands in it." Think lava pits, electric fences, a poison gas zone that closes in on a battle royale, or a kill plane below your parkour map that instantly eliminates anyone who falls.
Reach for it whenever you want passive, area-based damage rather than a one-shot hit. Out of the box you can set damage and the affected class/team in the device options, but in Verse you get much more: subscribe to AgentEntersEvent / AgentExitsEvent to run logic when players cross the boundary, change the damage live with SetDamage, query who's inside with GetAgentsInVolume and IsInVolume, and Enable/Disable the whole hazard during a round.
Because it inherits from effect_volume_device (which inherits from volume_device), it also carries the prop tracking events PropEnterEvent and PropExitEvent.
API Reference
damage_volume_device
Used to specify an area volume which can damage
agents, vehicles, and creatures.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from effect_volume_device.
damage_volume_device<public> := class<concrete><final>(effect_volume_device):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
AgentEntersEvent |
AgentEntersEvent<public>:listenable(agent) |
Signaled when an agent enters the volume. Sends the agent entering the volume. |
AgentExitsEvent |
AgentExitsEvent<public>:listenable(agent) |
Signaled when an agent exits the volume. Sends the agent exiting the volume. |
PropEnterEvent |
PropEnterEvent<public>:listenable(creative_prop) |
Signaled when a creative_prop enters the device volume. |
PropExitEvent |
PropExitEvent<public>:listenable(creative_prop) |
Signaled when a creative_prop exits the device volume. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
UpdateSelectedClass |
UpdateSelectedClass<public>(Agent:agent):void |
Updates Selected Class to Agent's class. |
UpdateSelectedTeam |
UpdateSelectedTeam<public>(Agent:agent):void |
Updates Selected Team to Agent's team. |
SetDamage |
SetDamage<public>(Damage:int):void |
Sets the damage to be applied each tick within the volume. Damage is clamped between 1 and 500. |
GetDamage |
GetDamage<public>()<transacts>:int |
Returns the damage to be applied each tick within the volume. |
IsInVolume |
IsInVolume<public>(Agent:agent)<transacts><decides>:void |
Is true when Agent is in the zone. |
GetAgentsInVolume |
GetAgentsInVolume<public>()<reads>:[]agent |
Returns an array of agents that are currently occupying the volume. |
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
Let's build a shrinking poison floor. When the round starts the damage volume begins disabled (safe). After a short delay it switches on, deals light damage, and ramps the damage up over time so stragglers get punished harder. Whenever a player enters the hazard we log them via a HUD message; when they leave we stop worrying about them.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# A poison floor that turns on after a delay and ramps up its damage.
poison_floor := class(creative_device):
# Drag the placed Damage Volume device into this slot in the editor.
@editable
DamageZone : damage_volume_device = damage_volume_device{}
# Optional HUD message device to announce hazard hits.
@editable
Warning : hud_message_device = hud_message_device{}
# localizes lets us pass a message to HUD APIs (no StringToMessage exists).
HazardText<localizes>(S:string):message = "{S}"
OnBegin<override>()<suspends>:void =
# React to players crossing the boundary.
DamageZone.AgentEntersEvent.Subscribe(OnAgentEntered)
DamageZone.AgentExitsEvent.Subscribe(OnAgentExited)
# Start the hazard disabled so nobody takes damage at spawn.
DamageZone.Disable()
Print("Poison floor armed but inactive")
# Wait, then turn it on with low damage.
Sleep(5.0)
DamageZone.SetDamage(10)
DamageZone.Enable()
Print("Poison floor ACTIVE: {DamageZone.GetDamage()} dmg/tick")
# Ramp damage up every few seconds.
RampDamage()
# Increase the per-tick damage in stages.
RampDamage()<suspends>:void =
loop:
Sleep(8.0)
CurrentDamage := DamageZone.GetDamage()
NewDamage := CurrentDamage + 25
# SetDamage clamps to 1..500 for us, so we can just feed it the sum.
DamageZone.SetDamage(NewDamage)
Print("Poison floor damage now {DamageZone.GetDamage()}")
# Runs every time an agent steps into the volume.
OnAgentEntered(Agent:agent):void =
Warning.Show(Agent, HazardText("You stepped in the poison! Get out!"))
Print("Agent entered the poison floor")
# Runs when an agent leaves the volume.
OnAgentExited(Agent:agent):void =
Print("Agent left the poison floor")
Line by line:
- The
@editablefieldsDamageZoneandWarninglet you wire the placed devices in the editor — without these fields, callingDamageZone.SetDamage(...)would fail with Unknown identifier. HazardText<localizes>(S:string):messageis the required pattern for turning a string into amessage— HUD APIs never take raw strings.- In
OnBeginweSubscribeour two handler methods to the enter/exit events. The handlers are ordinary class methods. DamageZone.Disable()makes the hazard inert at round start.Sleep(5.0)(legal becauseOnBeginis<suspends>) waits 5 seconds.SetDamage(10)thenEnable()activates the hazard at 10 damage per tick.GetDamage()reads back the current value to confirm.RampDamageloops forever, adding 25 damage every 8 seconds. BecauseSetDamageclamps to1..500, we never have to bounds-check ourselves.OnAgentEnteredfires the HUD warning to the specific player;OnAgentExitedjust logs.
Common patterns
Toggle the hazard with a query — only damage when someone's actually inside
Use GetAgentsInVolume and IsInVolume to inspect occupancy. Here we count occupants and disable the volume when it empties out (to save it for later), re-enabling on entry.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
smart_hazard := class(creative_device):
@editable
DamageZone : damage_volume_device = damage_volume_device{}
OnBegin<override>()<suspends>:void =
DamageZone.AgentEntersEvent.Subscribe(OnEntered)
DamageZone.AgentExitsEvent.Subscribe(OnExited)
OnEntered(Agent:agent):void =
# IsInVolume is a <decides> query — use it in an if.
if (DamageZone.IsInVolume[Agent]):
Print("Confirmed inside the hazard")
Occupants := DamageZone.GetAgentsInVolume()
Print("Agents in volume: {Occupants.Length}")
OnExited(Agent:agent):void =
Remaining := DamageZone.GetAgentsInVolume()
if (Remaining.Length = 0):
Print("Volume empty")
Lock the hazard to one player's team
UpdateSelectedTeam and UpdateSelectedClass retarget who the volume hurts based on a real agent. Here the first player to enter sets the team that the volume will damage.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
team_trap := class(creative_device):
@editable
DamageZone : damage_volume_device = damage_volume_device{}
OnBegin<override>()<suspends>:void =
DamageZone.SetDamage(50)
DamageZone.AgentEntersEvent.Subscribe(OnEntered)
OnEntered(Agent:agent):void =
# Retarget the volume's Selected Team/Class to the entering agent.
DamageZone.UpdateSelectedTeam(Agent)
DamageZone.UpdateSelectedClass(Agent)
Print("Hazard now tuned to this agent's team and class")
React to props passing through
The volume also tracks creative props via PropEnterEvent / PropExitEvent — handy for detecting a rolling boulder or thrown object crossing a kill plane.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
prop_watch := class(creative_device):
@editable
DamageZone : damage_volume_device = damage_volume_device{}
OnBegin<override>()<suspends>:void =
DamageZone.PropEnterEvent.Subscribe(OnPropIn)
DamageZone.PropExitEvent.Subscribe(OnPropOut)
OnPropIn(Prop:creative_prop):void =
Print("A prop entered the damage volume")
OnPropOut(Prop:creative_prop):void =
Print("A prop left the damage volume")
Gotchas
- You must declare an
@editablefield. Callingdamage_volume_device{}.SetDamage(...)against a literal does nothing useful — wire a placed device through an@editablefield and reference that. IsInVolumeis<decides>, not a boolean. It uses square brackets and must sit inside a failure context:if (DamageZone.IsInVolume[Agent]):. WritingIsInVolume(Agent)with parentheses won't compile.SetDamageclamps to 1..500. You can't deal 0 damage to "pause" the hazard — to stop damage entirely useDisable()instead, andEnable()to resume.- Damage is per tick, not per second. Standing in a volume with damage 10 applies 10 each tick, which adds up fast — start low and test.
- Enter/exit handlers receive a plain
agent. TheAgentEntersEventhands you a non-optionalAgent:agentdirectly, so noif (A := Agent?)unwrap is needed here. (That unwrap is only forlistenable(?agent)events.) - HUD/message params need a localized
message. There is noStringToMessage; declare a<localizes>helper likeHazardTextand passHazardText("..."). - Int vs float.
SetDamage/GetDamagedeal inint. The lower-levelDamage(Amount:float)interface takes a float — Verse won't auto-convert between them, so keep your hazard values as ints when callingSetDamage.