Overview
The progress_based_mesh_device associates a numeric progression with a sequence of meshes. You define a set of Threshold Meshes under the Visuals category in UEFN, and as the device's CurrentProgress (a float from 0 to 100) rises and falls, it transitions between those meshes. Think of it as a visual progress bar made of real 3D geometry.
Reach for it whenever you want a build to show its completion state physically: a fortress wall that materializes as players deposit resources, a magic portal that solidifies as a ritual completes, a flooding chamber, or a stadium that constructs itself between rounds.
You don't move progress by calling methods — this device has no methods. Instead you read and write the var properties (CurrentProgress, ProgressTarget, ProgressRate, ProgressState) and react to four events:
- FillEvent — fires when
CurrentProgressreachesProgressTarget. - EmptyEvent — fires when
CurrentProgressreaches 0. - ProgressChangeEvent — fires every time
CurrentProgresschanges, handing you the new float value. - ProgressThresholdCrossEvent — fires when a new mesh threshold is crossed (the visual actually changes), handing you the crossed value.
The device can advance itself automatically: set ProgressState to progress_device_state.Progress and it climbs by ProgressRate per second; set it to Regress to fall; Pause holds it still. Or you can ignore that and drive CurrentProgress directly from your own game logic.
API Reference
progress_based_mesh_device
This device is used to associate changes in progression with a mesh transition. As the device's progression changes, it will transition between a set of defined meshes. The ThresholdMesh field can be found under the 'Visuals' category on an instance of a progress_based_mesh_device in Unreal Editor Fortnite.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
progress_based_mesh_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
FillEvent |
FillEvent<public>:listenable(tuple()) |
This event is fired whenever 'CurrentProgress' reaches the 'ProgressTarget'. |
EmptyEvent |
EmptyEvent<public>:listenable(tuple()) |
This event is fired whenever 'CurrentProgress' reaches 0. |
ProgressChangeEvent |
ProgressChangeEvent<public>:listenable(float) |
This event is fired whenever 'CurrentProgress' value gets changed, whether through direct modification, or as a result of the Progress or Regress state. |
ProgressThresholdCrossEvent |
ProgressThresholdCrossEvent<public>:listenable(float) |
# This event fires whenever this device changes meshes as a result of 'CurrentProgress' value causing a new Threshold to be met. The ThresholdMeshe field can be found under the 'Visuals' category on an instance of a progress_based_mesh_devi |
Walkthrough
Let's build a resource bridge. Players step on a trigger to deposit a plank's worth of progress. Each deposit nudges CurrentProgress up by 20. As progress crosses thresholds, the bridge mesh assembles. When it's fully built, we grant the players access by playing a sound and logging the completion. If the bridge sits unfinished too long it can be regressed — we wire EmptyEvent to reset state.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
bridge_builder := class(creative_device):
# The progress-driven mesh that visually assembles the bridge.
@editable
BridgeMesh : progress_based_mesh_device = progress_based_mesh_device{}
# Players step here to deposit a plank.
@editable
DepositPad : trigger_device = trigger_device{}
# Plays once the bridge is fully built.
@editable
CompleteSound : audio_player_device = audio_player_device{}
OnBegin<override>()<suspends> : void =
# Make sure the bridge starts empty and targets a full 100.
set BridgeMesh.CurrentProgress = 0.0
set BridgeMesh.ProgressTarget = 100.0
# React to deposits.
DepositPad.TriggeredEvent.Subscribe(OnDeposit)
# React to the bridge's own progression events.
BridgeMesh.ProgressChangeEvent.Subscribe(OnProgressChanged)
BridgeMesh.ProgressThresholdCrossEvent.Subscribe(OnThresholdCrossed)
BridgeMesh.FillEvent.Subscribe(OnBridgeComplete)
BridgeMesh.EmptyEvent.Subscribe(OnBridgeEmpty)
# Each step on the pad adds one plank's worth of progress.
OnDeposit(Agent : ?agent) : void =
NewProgress := BridgeMesh.CurrentProgress + 20.0
# CurrentProgress is clamped by the device against ProgressTarget,
# but we keep it tidy here too.
set BridgeMesh.CurrentProgress = NewProgress
# Fires on every change to CurrentProgress.
OnProgressChanged(NewValue : float) : void =
Print("Bridge progress is now: {NewValue}")
# Fires only when the visible mesh actually swaps to a new threshold.
OnThresholdCrossed(CrossedValue : float) : void =
Print("New bridge section appeared at threshold {CrossedValue}")
# Fires when CurrentProgress reaches ProgressTarget (100).
OnBridgeComplete() : void =
Print("Bridge fully built — players may cross!")
CompleteSound.Play()
# Fires when CurrentProgress reaches 0.
OnBridgeEmpty() : void =
Print("Bridge collapsed back to nothing.")
Line by line:
- The three
@editablefields let us drop the real placed devices into this script's details panel in UEFN.BridgeMeshis our star device. - In
OnBeginwe seed the device state by writing itsvarproperties:set BridgeMesh.CurrentProgress = 0.0andset BridgeMesh.ProgressTarget = 100.0. Note the.0— Verse will not convert0(int) to0.0(float) for you. - We subscribe
OnDepositto the trigger so each step runs our logic. - We subscribe one handler per device event. Each subscription points at a method declared at class scope.
OnDepositreceives(Agent : ?agent)because alistenable(?agent)event hands you an optional agent. We don't need the agent here, so we don't unwrap it — we just bump progress.OnProgressChangedreceives a plainfloat(the new value) becauseProgressChangeEventislistenable(float).OnThresholdCrossedalso receives afloat, but it only fires when the mesh changes — perfect for a "new section appeared" sound or VFX.OnBridgeCompleteandOnBridgeEmptytake no parameters becauseFillEventandEmptyEventarelistenable(tuple())— an empty tuple carries no data.
Common patterns
Auto-progressing dam that fills over time
Instead of manual deposits, let the device climb on its own using ProgressState and ProgressRate. Here a dam starts filling the moment the game begins.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
auto_dam := class(creative_device):
@editable
DamMesh : progress_based_mesh_device = progress_based_mesh_device{}
OnBegin<override>()<suspends> : void =
# 5 progress per second toward a full 100.
set DamMesh.ProgressRate = 5.0
set DamMesh.ProgressTarget = 100.0
# Start the automatic climb.
set DamMesh.ProgressState = progress_device_state.Progress
DamMesh.FillEvent.Subscribe(OnDamFull)
OnDamFull() : void =
# Once full, hold it in place.
set DamMesh.ProgressState = progress_device_state.Pause
Print("Dam is full — flood gates ready.")
Tracking the live value with ProgressChangeEvent
Mirror the device's float into a Billboard-style readout or your own logic. This snippet just logs a percentage every time it changes.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
progress_reporter := class(creative_device):
@editable
Monument : progress_based_mesh_device = progress_based_mesh_device{}
OnBegin<override>()<suspends> : void =
Monument.ProgressChangeEvent.Subscribe(OnChanged)
OnChanged(Value : float) : void =
# Value is already 0..100, so it reads as a percent.
Print("Monument is {Value}% complete")
Reacting only when the mesh visibly changes
Use ProgressThresholdCrossEvent to trigger one-shot effects exactly when a new chunk of geometry appears — cheaper than firing on every tiny change.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
portal_assembler := class(creative_device):
@editable
Portal : progress_based_mesh_device = progress_based_mesh_device{}
@editable
StageSound : audio_player_device = audio_player_device{}
OnBegin<override>()<suspends> : void =
Portal.ProgressThresholdCrossEvent.Subscribe(OnStageReached)
OnStageReached(Threshold : float) : void =
# A new portal stage materialized — punch a sound cue.
StageSound.Play()
Print("Portal advanced to a new visual stage at {Threshold}")
Gotchas
- No methods to call. Unlike most devices, you do not call
Progress()orSetProgress(). You write thevarproperties (set Device.CurrentProgress = ...,set Device.ProgressState = ...) and subscribe to the events. Trying to call a method on this device is an 'Unknown identifier' error. - Floats only.
CurrentProgress,ProgressTarget, andProgressRateare allfloat. Write0.0,100.0,5.0— never bare integers. Verse does not auto-convert int↔float. - ProgressTarget is clamped 0–100. Setting it lower than the current
CurrentProgresssnapsCurrentProgressdown to match and fires any threshold/empty events that result. Set your target before driving progress to avoid surprise events. - FillEvent / EmptyEvent take
tuple()— no parameters. Declare their handlers with an empty parameter list (OnBridgeComplete() : void =). Adding an(Agent : ?agent)param will not compile against these events. - ProgressChangeEvent vs ProgressThresholdCrossEvent. ProgressChange fires on every value change — potentially many times per second when auto-progressing. ProgressThresholdCross fires only when the mesh swaps. Use the threshold event for expensive one-shot effects so you don't spam VFX/audio.
- Editable fields must be on a placed device. The
progress_based_mesh_device{}default is just a placeholder; you must assign the actual placed device in the UEFN details panel, or events never fire. - Set state in OnBegin if you want the climb to start automatically. The device defaults to
Pause; you mustset Device.ProgressState = progress_device_state.ProgressforProgressRateto take effect.