Overview
The vine_rail_device creates a customizable vine version of the classic Grind Rail. Players can grab onto it and slide along its path, making it perfect for traversal puzzles, obstacle courses, jungle-themed maps, and momentum-based challenges.
When do you reach for it?
- Timed vine challenges — start a countdown the moment a player grabs the vine, stop it when they dismount.
- Gated shortcuts — keep the vine disabled until the player completes a prior objective, then call
Enable()to open the route. - Hidden paths — call
Hide()so the vine is invisible but still rideable, revealing it as a reward withShow(). - Score or buff triggers — grant items or apply effects the instant a player starts or finishes a grind.
The device exposes:
StartedGrindingEvent— fires when a player first grabs the vine.EndedGrindingEvent— fires when a player leaves the vine (dismount, fall off, or reach the end).Enable()/Disable()— toggle whether the vine is rideable.Hide()/Show()— toggle visibility (note: a hidden vine can still be ridden if enabled).
API Reference
vine_rail_device
Used to create a customizable vine version of the Grind Rails.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
vine_rail_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
StartedGrindingEvent |
StartedGrindingEvent<public>:listenable(agent) |
Signaled when an agent starts grinding on this vine_rail_device. |
EndedGrindingEvent |
EndedGrindingEvent<public>:listenable(agent) |
Signaled when an agent starts grinding on this vine_rail_device. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device, letting players grind on the vines. |
Disable |
Disable<public>():void |
Disables this device, preventing players from grinding on the vines. |
Hide |
Hide<public>():void |
Hides the track. Players can still grind on the track if it is enabled. |
Show |
Show<public>():void |
Make this track visible. |
Walkthrough
Scenario: A jungle obstacle course has a vine rail that is initially locked. When the player steps on a trigger plate, the vine becomes active and visible. The moment the player grabs the vine a countdown HUD message is implied (here we track the grind with Print stand-ins replaced by real device calls). When the player finishes the grind, the vine is hidden and disabled again — a one-shot secret shortcut.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# Localizer helper so we can pass messages to devices that require `message`
vine_unlock_manager_device := class(creative_device):
# The vine rail placed in the level — wire this up in the UEFN Details panel
@editable
VineRail : vine_rail_device = vine_rail_device{}
# A trigger plate the player must step on to unlock the vine
@editable
UnlockPlate : trigger_device = trigger_device{}
# Called once at game start
OnBegin<override>()<suspends> : void =
# Start with the vine disabled and hidden — it's a secret shortcut
VineRail.Disable()
VineRail.Hide()
# When the player steps on the unlock plate, open the vine
UnlockPlate.TriggeredEvent.Subscribe(OnPlateTriggered)
# React to the player grabbing the vine
VineRail.StartedGrindingEvent.Subscribe(OnVineGrabbed)
# React to the player finishing (or falling off) the vine
VineRail.EndedGrindingEvent.Subscribe(OnVineReleased)
# Handler: unlock plate stepped on
OnPlateTriggered(Agent : ?agent) : void =
# Reveal and enable the vine for one use
VineRail.Show()
VineRail.Enable()
# Handler: player grabbed the vine
# StartedGrindingEvent is listenable(agent), so the payload is a plain agent
OnVineGrabbed(Grinder : agent) : void =
# The vine is now in use — disable the unlock plate so no one else triggers it
UnlockPlate.Disable()
# Handler: player left the vine
OnVineReleased(Grinder : agent) : void =
# Hide and lock the vine again — one-shot shortcut is spent
VineRail.Disable()
VineRail.Hide()
Line-by-line breakdown:
| Lines | What's happening |
|---|---|
@editable VineRail |
Declares the vine rail reference. You MUST wire this in the UEFN Details panel — bare identifiers won't work. |
@editable UnlockPlate |
The trigger plate that gates access to the vine. |
VineRail.Disable() / VineRail.Hide() |
Called in OnBegin so the vine starts locked and invisible. |
UnlockPlate.TriggeredEvent.Subscribe(OnPlateTriggered) |
Subscribes to the plate; the handler signature matches listenable(?agent). |
VineRail.StartedGrindingEvent.Subscribe(OnVineGrabbed) |
Subscribes to the vine grab event. |
VineRail.EndedGrindingEvent.Subscribe(OnVineReleased) |
Subscribes to the vine release event. |
OnVineGrabbed(Grinder : agent) |
StartedGrindingEvent is listenable(agent) — the payload is a plain agent, no option unwrap needed. |
VineRail.Show() / VineRail.Enable() |
Reveals and activates the vine when the plate is stepped on. |
VineRail.Disable() / VineRail.Hide() |
Locks the vine away again after the grind ends. |
Common patterns
Pattern 1 — Score multiplier while riding the vine
Grant a player a score bonus every second they stay on the vine. Uses StartedGrindingEvent and EndedGrindingEvent together with a race to cancel the scoring loop the moment they dismount.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
vine_score_multiplier_device := class(creative_device):
@editable
VineRail : vine_rail_device = vine_rail_device{}
@editable
ScoreManager : score_manager_device = score_manager_device{}
OnBegin<override>()<suspends> : void =
VineRail.StartedGrindingEvent.Subscribe(OnGrindStart)
VineRail.EndedGrindingEvent.Subscribe(OnGrindEnd)
# Tracks whether the grind is active so we can cancel the scoring loop
var GrindActive : logic = false
OnGrindStart(Grinder : agent) : void =
set GrindActive = true
# Kick off a concurrent scoring loop for this grinder
spawn { ScoreLoop(Grinder) }
OnGrindEnd(Grinder : agent) : void =
set GrindActive = false
# Awards points every second the player stays on the vine
ScoreLoop(Grinder : agent)<suspends> : void =
loop:
if (not GrindActive?):
break
Sleep(1.0)
if (GrindActive?):
ScoreManager.Activate(Grinder)
Note:
logicin Verse is the boolean type.not GrindActive?checks the falsy branch.spawnlaunches the async loop without blockingOnGrindStart.
Pattern 2 — Toggling vine visibility as a puzzle reveal
Uses Hide() and Show() independently of Enable/Disable — the vine stays rideable the whole time, but is invisible until the player solves a puzzle (here represented by a second trigger plate).
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
vine_reveal_puzzle_device := class(creative_device):
@editable
VineRail : vine_rail_device = vine_rail_device{}
# Puzzle completion plate — wire to whatever objective you like
@editable
PuzzlePlate : trigger_device = trigger_device{}
OnBegin<override>()<suspends> : void =
# Vine is enabled (rideable) but invisible — a hidden path
VineRail.Enable()
VineRail.Hide()
PuzzlePlate.TriggeredEvent.Subscribe(OnPuzzleSolved)
OnPuzzleSolved(Agent : ?agent) : void =
# Reveal the vine visually; it was already rideable
VineRail.Show()
# Disable the plate so the reveal only fires once
PuzzlePlate.Disable()
Pattern 3 — Disabling the vine mid-game as a hazard
A game-manager device that disables a vine rail after a countdown, turning a safe traversal route into a trap. Demonstrates Disable() called from a timed async context.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
vine_hazard_timer_device := class(creative_device):
@editable
VineRail : vine_rail_device = vine_rail_device{}
# How many seconds before the vine is cut
@editable
CountdownSeconds : float = 30.0
OnBegin<override>()<suspends> : void =
VineRail.Enable()
VineRail.Show()
# Wait for the countdown, then cut the vine
Sleep(CountdownSeconds)
VineRail.Disable()
VineRail.Hide()
Gotchas
1. @editable is mandatory — you cannot use a bare identifier
Every vine_rail_device you want to call methods on must be declared as an @editable field inside your class(creative_device) and wired in the UEFN Details panel. Writing vine_rail_device{}.Enable() compiles but does nothing — it creates a throwaway instance with no placed device behind it.
2. Event payload is agent, not ?agent
StartedGrindingEvent and EndedGrindingEvent are both listenable(agent) — the payload is a plain, non-optional agent. Your handler signature must be (Grinder : agent) : void, not (Grinder : ?agent) : void. Contrast this with trigger_device.TriggeredEvent which is listenable(?agent) and does require an option unwrap.
3. Hide() does NOT disable grinding
A hidden vine rail is still rideable if Enable() has been called. This is intentional and can be a powerful design tool (invisible vine paths), but it also means you must call both Hide() and Disable() if you want to fully remove the vine from play.
4. EndedGrindingEvent fires on ANY exit
The event fires whether the player reached the end of the vine, jumped off mid-grind, or was eliminated. If your logic depends on a successful full-grind completion, you'll need additional state tracking (e.g., a position check or a separate trigger at the vine's endpoint).
5. Async handlers need spawn
Event handler callbacks (subscribed via Subscribe) are synchronous — they cannot themselves be <suspends>. If you need to do async work in response to a grind event (like Sleep then award a bonus), launch a concurrent task with spawn { MyAsyncFunction(Grinder) } inside the handler.
6. message parameters require a localizer
If you pass text to any device that accepts a message type (e.g., a HUD message device), you cannot pass a raw string. Declare a localizer: MyText<localizes>(S:string):message = "{S}" and call MyText("Vine grabbed!"). There is no StringToMessage function in Verse.