Overview
The physics_tree_device is a physics-simulated tree that players (or game logic) can knock down. When it falls it splits into two separate objects — a log and a stump — each with its own lifetime and its own Verse event. This makes it uniquely useful for:
- Timed lumberjack mini-games — track how many trees a player fells in 60 seconds.
- Environmental hazards — a falling log that damages players or blocks a corridor.
- Puzzle gates — a barrier that only clears once the stump is destroyed.
- Cleanup logic — auto-destroy logs and stumps after a delay so the island stays tidy.
Because the device fires discrete events at each phase (TreeSpawnedEvent, TreeKnockedDownEvent, LogDestroyedEvent, StumpDestroyedEvent) and exposes ReleaseLog, DestroyLog, and DestroyStump, you can script the full lifecycle from Verse without any channel wiring.
API Reference
physics_tree_device
Physics tree that can be chopped down, and damage players, 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_tree_device<public> := class<concrete><final>(physics_object_base_device):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
TreeSpawnedEvent |
TreeSpawnedEvent<public>:listenable(tuple()) |
Signaled when a tree is spawned. |
LogDestroyedEvent |
LogDestroyedEvent<public>:listenable(tuple()) |
Signaled when the log created by a tree is destroyed. |
StumpDestroyedEvent |
StumpDestroyedEvent<public>:listenable(tuple()) |
Signaled when the stump created by a tree is destroyed. |
TreeKnockedDownEvent |
TreeKnockedDownEvent<public>:listenable(tuple()) |
Signaled when a tree has taken enough damage to be knocked down. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
ReleaseLog |
ReleaseLog<public>():void |
Releases the log from the tree, if there is one. |
DestroyLog |
DestroyLog<public>():void |
Destroys the current log. |
DestroyStump |
DestroyStump<public>():void |
Destroys the current stump. |
Walkthrough
Scenario: Lumberjack Hazard Course
A tree stands in the middle of a gauntlet. When it is knocked down the log is automatically released (so it rolls and becomes a hazard), a 5-second countdown starts, and then both the log and stump are cleaned up so the next wave can reset cleanly. A message is printed to the log at each stage so you can follow the lifecycle in the Output Log.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
# Attach this device to a physics_tree_device placed in the scene.
# Wire Tree → this device's @editable field in the Details panel.
lumberjack_hazard_manager := class(creative_device):
# Reference the placed physics_tree_device in the UEFN Details panel.
@editable
Tree : physics_tree_device = physics_tree_device{}
# How many seconds after the tree falls before we clean up.
@editable
CleanupDelaySec : float = 5.0
# ── Lifecycle ────────────────────────────────────────────────────────────
OnBegin<override>()<suspends> : void =
# Subscribe to all four tree events.
Tree.TreeSpawnedEvent.Subscribe(OnTreeSpawned)
Tree.TreeKnockedDownEvent.Subscribe(OnTreeKnockedDown)
Tree.LogDestroyedEvent.Subscribe(OnLogDestroyed)
Tree.StumpDestroyedEvent.Subscribe(OnStumpDestroyed)
# ── Event handlers ───────────────────────────────────────────────────────
# Called when the tree first appears (or respawns).
OnTreeSpawned(Args : tuple()) : void =
Print("[Tree] Tree has spawned — ready to be chopped!")
# Called the moment the tree takes enough damage to fall.
# We release the log immediately so it rolls as a physics hazard,
# then schedule cleanup after a delay.
OnTreeKnockedDown(Args : tuple()) : void =
Print("[Tree] Tree knocked down — releasing log as hazard!")
Tree.ReleaseLog() # detach the log so it rolls freely
spawn { CleanupAfterDelay() }
# Called when the log object is destroyed.
OnLogDestroyed(Args : tuple()) : void =
Print("[Tree] Log destroyed.")
# Called when the stump object is destroyed.
OnStumpDestroyed(Args : tuple()) : void =
Print("[Tree] Stump destroyed — area is clear for next wave.")
# ── Helpers ──────────────────────────────────────────────────────────────
# Waits CleanupDelaySec seconds, then destroys whatever remains.
CleanupAfterDelay()<suspends> : void =
Sleep(CleanupDelaySec)
Print("[Tree] Cleanup timer fired — destroying log and stump.")
Tree.DestroyLog() # remove the log prop
Tree.DestroyStump() # remove the stump prop
Line-by-line breakdown
| Lines | What's happening |
|---|---|
@editable Tree |
Exposes the physics_tree_device reference so you can wire it in the UEFN Details panel. |
OnBegin |
Subscribes all four event handlers before any gameplay starts. |
OnTreeSpawned |
Fires when the tree is first placed or respawns — good for resetting counters. |
OnTreeKnockedDown |
The key gameplay moment. We call ReleaseLog() immediately so the log becomes a live physics prop that can roll and crush. |
spawn { CleanupAfterDelay() } |
Launches the cleanup coroutine without blocking the event handler. |
Sleep(CleanupDelaySec) |
Non-blocking wait — the island keeps running normally. |
DestroyLog() / DestroyStump() |
Remove the remaining props so the area resets for the next wave. |
OnLogDestroyed / OnStumpDestroyed |
Confirm destruction — you could trigger score events or spawn pickups here. |
Common patterns
Pattern 1 — Score counter: count trees felled per player session
Subscribe to TreeKnockedDownEvent across multiple trees and increment a shared counter. This pattern shows how to manage an array of trees and aggregate their events.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
tree_score_tracker := class(creative_device):
# Wire up to three trees in the Details panel.
@editable
Trees : []physics_tree_device = array{}
var TreesFelled : int = 0
OnBegin<override>()<suspends> : void =
# Subscribe to every tree's knocked-down event.
for (T : Trees):
T.TreeKnockedDownEvent.Subscribe(OnAnyTreeFelled)
OnAnyTreeFelled(Args : tuple()) : void =
set TreesFelled += 1
Print("Trees felled this session: {TreesFelled}")
What this covers: TreeKnockedDownEvent on multiple devices, array iteration with for, and a running counter.
Pattern 2 — Instant cleanup: destroy log and stump the moment the tree falls
Sometimes you want the tree to vanish immediately — for example, a puzzle tree that should disappear the instant it is cut so a path opens up.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
instant_clear_tree := class(creative_device):
@editable
PuzzleTree : physics_tree_device = physics_tree_device{}
OnBegin<override>()<suspends> : void =
PuzzleTree.TreeKnockedDownEvent.Subscribe(OnPuzzleTreeFelled)
# As soon as the tree falls, wipe both the log and the stump
# so the path is immediately clear.
OnPuzzleTreeFelled(Args : tuple()) : void =
PuzzleTree.DestroyLog()
PuzzleTree.DestroyStump()
Print("[Puzzle] Path cleared — log and stump removed instantly.")
What this covers: Calling DestroyLog() and DestroyStump() together in a single handler for instant cleanup.
Pattern 3 — Stump watch: trigger a reward when the stump is finally gone
Some game modes want players to also destroy the stump (e.g., with explosives) before a reward spawns. Subscribe to StumpDestroyedEvent and LogDestroyedEvent to gate a reward on full cleanup.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
stump_reward_gate := class(creative_device):
@editable
RewardTree : physics_tree_device = physics_tree_device{}
# An item granter wired in the Details panel — fires when stump is gone.
@editable
Granter : item_granter_device = item_granter_device{}
var LogGone : logic = false
var StumpGone : logic = false
OnBegin<override>()<suspends> : void =
RewardTree.LogDestroyedEvent.Subscribe(OnLogGone)
RewardTree.StumpDestroyedEvent.Subscribe(OnStumpGone)
OnLogGone(Args : tuple()) : void =
set LogGone = true
Print("[Reward] Log cleared.")
CheckBothGone()
OnStumpGone(Args : tuple()) : void =
set StumpGone = true
Print("[Reward] Stump cleared.")
CheckBothGone()
# Only grant the reward once BOTH the log AND stump are destroyed.
CheckBothGone() : void =
if (LogGone? and StumpGone?):
Print("[Reward] Full cleanup complete — granting reward!")
Granter.GrantItem()
What this covers: LogDestroyedEvent and StumpDestroyedEvent used together to gate a downstream action, plus simple boolean state tracking.
Gotchas
1. Event handlers receive tuple(), not an agent
All four events on physics_tree_device are typed listenable(tuple()) — they carry no payload. Your handler signature must be (Args : tuple()) : void. Do not try to extract an agent from them; there is none.
# ✅ Correct
OnTreeFelled(Args : tuple()) : void = ...
# ❌ Wrong — will not compile
OnTreeFelled(Agent : agent) : void = ...
2. ReleaseLog vs DestroyLog — they are not the same
ReleaseLog()detaches the log from the stump and lets physics take over — the log rolls, slides, and can damage players. The log prop still exists.DestroyLog()removes the log prop entirely from the world.
Calling DestroyLog() before ReleaseLog() is fine; calling ReleaseLog() after DestroyLog() is a no-op (there is no log left to release).
3. Call order matters for cleanup
If you call DestroyLog() and DestroyStump() inside OnTreeKnockedDown, the tree is removed almost instantly — before the physics simulation has a chance to play. This is intentional for puzzle gates but wrong for hazard courses. Use Sleep() in a spawned coroutine when you want the physics to play out first.
4. @editable is mandatory — you cannot use a bare identifier
You must declare Tree : physics_tree_device as an @editable field inside your creative_device class and wire it in the UEFN Details panel. A bare physics_tree_device{} creates a default (non-placed) instance that has no connection to the scene object and will silently do nothing.
5. spawn {} is required for <suspends> helpers called from event handlers
Event handlers are not <suspends> contexts. If your cleanup logic calls Sleep(), wrap it in spawn { MyHelper() }. Forgetting this causes a compile error about calling a suspending function from a non-suspending context.
6. Stump and log events only fire if the objects are actually destroyed
LogDestroyedEvent fires when the log prop is destroyed — either by DestroyLog() or by in-world damage. If you never destroy the log (e.g., you only called ReleaseLog()), LogDestroyedEvent will not fire until something else destroys it. Design your reward gates accordingly.