Overview
The physics_boulder_device places a large boulder that sits balanced on a base. When released, it rolls and tumbles, dealing damage to anything in its path — agents, vehicles, creatures, and structures. It's the classic "run from the rolling boulder" hazard.
Reach for this device whenever you want a dramatic, physics-driven obstacle that you control from gameplay: a temple trap that triggers when a player grabs the idol, a timed survival round where boulders release one after another, or a destructible objective where players must destroy the base before the boulder is unleashed.
Unlike a trigger or button, you don't "activate" it once — you orchestrate three distinct actions:
- ReleaseRollingBoulder() — let the balanced boulder roll free.
- DestroyBase() — blow up the base the boulder rests on.
- DestroyRollingBoulder() — remove a boulder that's already rolling.
And it tells you what's happening through four events: when the balanced boulder spawns, when it's destroyed, when the base is destroyed, and when a rolling boulder is destroyed. This article shows you how to wire all of those up in a real trap scenario.
API Reference
physics_boulder_device
Physics boulder that can be dislodged and damage
agents, vehicles, creatures, and structures.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from physics_object_base_device.
physics_boulder_device<public> := class<concrete><final>(physics_object_base_device):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
BalancedBoulderSpawnedEvent |
BalancedBoulderSpawnedEvent<public>:listenable(tuple()) |
Signaled when the balanced boulder is spawned on the base. |
BalancedBoulderDestroyedEvent |
BalancedBoulderDestroyedEvent<public>:listenable(tuple()) |
Signaled when the balanced boulder is destroyed. |
BaseDestroyedEvent |
BaseDestroyedEvent<public>:listenable(tuple()) |
Signaled when the base of the boulder is destroyed. |
RollingBoulderDestroyedEvent |
RollingBoulderDestroyedEvent<public>:listenable(tuple()) |
Signaled when the rolling boulder is destroyed. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
DestroyBase |
DestroyBase<public>():void |
Destroys the boulder's base. |
ReleaseRollingBoulder |
ReleaseRollingBoulder<public>():void |
Releases the boulder sitting on the base, if there is one. |
DestroyRollingBoulder |
DestroyRollingBoulder<public>():void |
Destroys the current rolling boulder. |
Walkthrough
Let's build a temple trap. A player steps on a pressure plate (a trigger_device). That releases the boulder to roll down the corridor. We listen for the boulder spawning back on its base (so we know the trap is armed again), and we light up a scoreboard-style message device whenever the rolling boulder is finally destroyed (it rolled into a wall and broke apart). We also give the player a panic button: a second trigger that destroys the rolling boulder mid-roll, so they can survive if they react fast.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
temple_trap := class(creative_device):
# The boulder hazard we control.
@editable
Boulder : physics_boulder_device = physics_boulder_device{}
# Step on this plate to spring the trap.
@editable
PressurePlate : trigger_device = trigger_device{}
# Emergency button: destroys the rolling boulder mid-roll.
@editable
PanicButton : trigger_device = trigger_device{}
# Shows status text to players.
@editable
Sign : hud_message_device = hud_message_device{}
Status<localizes>(S:string):message = "{S}"
OnBegin<override>()<suspends>:void =
# When a player steps on the plate, release the boulder.
PressurePlate.TriggeredEvent.Subscribe(OnPlateStepped)
# The panic button destroys whatever boulder is rolling.
PanicButton.TriggeredEvent.Subscribe(OnPanic)
# React to the boulder's lifecycle.
Boulder.BalancedBoulderSpawnedEvent.Subscribe(OnBoulderArmed)
Boulder.RollingBoulderDestroyedEvent.Subscribe(OnBoulderBroken)
# Handler for the pressure plate. The payload is the agent who stepped on it.
OnPlateStepped(Agent : ?agent):void =
if (Player := Agent?):
Sign.Show(Player, Status("The ground rumbles..."))
# Let the balanced boulder roll free down the corridor.
Boulder.ReleaseRollingBoulder()
# Handler for the panic button.
OnPanic(Agent : ?agent):void =
# Destroy the rolling boulder before it crushes anyone.
Boulder.DestroyRollingBoulder()
# Fires when a fresh balanced boulder appears on the base — trap is armed again.
OnBoulderArmed():void =
Sign.Show(Status("The trap is armed."))
# Fires when a rolling boulder is destroyed (hit a wall, or we panic-destroyed it).
OnBoulderBroken():void =
Sign.Show(Status("The boulder shattered. You're safe... for now."))
Line by line:
- The four
@editablefields let you drop this script in the level and point each one at a placed device in the Details panel. Status<localizes>(S:string):messageis our helper for turning a plain string into themessagetype thathud_message_device.Showrequires — there is noStringToMessagein Verse.- In
OnBegin, we subscribe every handler. Note event handlers are ordinary methods at class scope; you only wire them up here. OnPlateSteppedreceives(Agent : ?agent)becausetrigger_device.TriggeredEventis alistenable(?agent). We unwrap withif (Player := Agent?)before using the player. Then we callReleaseRollingBoulder()— the boulder rolls.OnPaniccallsDestroyRollingBoulder()to delete the rolling boulder mid-flight.OnBoulderArmedis the handler forBalancedBoulderSpawnedEvent, whose payload is an emptytuple()— so the method takes no parameters.OnBoulderBrokenhandlesRollingBoulderDestroyedEvent, also atuple()event.Sign.Show(Status("..."))with no agent broadcasts to everyone.
Common patterns
Destroy the base to win an objective
Make the boulder's base a destructible objective: when players hold a button, you blow up the base and clear the hazard for good. Listen to BaseDestroyedEvent to award points or open the next door.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
base_breaker := class(creative_device):
@editable
Boulder : physics_boulder_device = physics_boulder_device{}
@editable
DemolishButton : button_device = button_device{}
@editable
NextDoor : door_device = door_device{}
OnBegin<override>()<suspends>:void =
DemolishButton.InteractedWithEvent.Subscribe(OnDemolish)
Boulder.BaseDestroyedEvent.Subscribe(OnBaseGone)
OnDemolish(Agent : agent):void =
# Destroy the base that holds the boulder.
Boulder.DestroyBase()
OnBaseGone():void =
# The hazard is cleared — open the path forward.
NextDoor.Open()
Track when the boulder is destroyed to spawn the next wave
A survival gauntlet: each time the balanced boulder is destroyed, advance the round. BalancedBoulderDestroyedEvent fires when the boulder on the base is removed.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
gauntlet_round := class(creative_device):
@editable
Boulder : physics_boulder_device = physics_boulder_device{}
@editable
StartTrigger : trigger_device = trigger_device{}
var Round : int = 0
OnBegin<override>()<suspends>:void =
StartTrigger.TriggeredEvent.Subscribe(OnStart)
Boulder.BalancedBoulderDestroyedEvent.Subscribe(OnBalancedGone)
OnStart(Agent : ?agent):void =
Boulder.ReleaseRollingBoulder()
OnBalancedGone():void =
set Round = Round + 1
# In a full game you'd spawn a new boulder set or scale difficulty here.
Print("Round advanced to {Round}")
Gotchas
- You must declare the boulder as an
@editablefield inside yourclass(creative_device)and assign the placed device in the Details panel. Callingphysics_boulder_device{}.ReleaseRollingBoulder()on a fresh literal does nothing — it isn't the boulder in your level. - The boulder events carry no payload.
BalancedBoulderSpawnedEvent,BalancedBoulderDestroyedEvent,BaseDestroyedEvent, andRollingBoulderDestroyedEventare alllistenable(tuple()). Their handlers take no parameters — don't write(Agent : ?agent)for them or it won't compile. trigger_device.TriggeredEventDOES carry?agent. That handler needs(Agent : ?agent)and you must unwrap it withif (Player := Agent?):before using the player. Mixing these two payload shapes up is the most common compile error.ReleaseRollingBoulder()only works if a boulder is sitting on the base. If the boulder has already rolled (or you destroyed the base), there's nothing to release. Listen toBalancedBoulderSpawnedEventto know when it's safe to release again.hud_message_device.Showneeds amessage, not astring. Use a<localizes>helper likeStatus<localizes>(S:string):message = "{S}"and passStatus("...")— there is noStringToMessagefunction.DestroyRollingBoulder()vsDestroyBase()are different actions: the first removes a boulder that's already in motion, the second removes the platform/base. Destroying the base does not automatically clean up an already-rolling boulder.