Reference Devices compiles

progress_based_mesh_device: Meshes That Build Themselves

The progress_based_mesh_device turns a single number — CurrentProgress — into a visual story. As progress climbs from 0 to 100, the device swaps through a stack of meshes you configure under Visuals, so a bridge can assemble plank by plank, a dam can fill, or a monument can rise. This article shows how to wire its events to real gameplay.

Updated Examples verified on the live UEFN compiler
Watch the Knotprogress_based_mesh_device in ~90 seconds.

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 CurrentProgress reaches ProgressTarget.
  • EmptyEvent — fires when CurrentProgress reaches 0.
  • ProgressChangeEvent — fires every time CurrentProgress changes, 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 @editable fields let us drop the real placed devices into this script's details panel in UEFN. BridgeMesh is our star device.
  • In OnBegin we seed the device state by writing its var properties: set BridgeMesh.CurrentProgress = 0.0 and set BridgeMesh.ProgressTarget = 100.0. Note the .0 — Verse will not convert 0 (int) to 0.0 (float) for you.
  • We subscribe OnDeposit to 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.
  • OnDeposit receives (Agent : ?agent) because a listenable(?agent) event hands you an optional agent. We don't need the agent here, so we don't unwrap it — we just bump progress.
  • OnProgressChanged receives a plain float (the new value) because ProgressChangeEvent is listenable(float).
  • OnThresholdCrossed also receives a float, but it only fires when the mesh changes — perfect for a "new section appeared" sound or VFX.
  • OnBridgeComplete and OnBridgeEmpty take no parameters because FillEvent and EmptyEvent are listenable(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() or SetProgress(). You write the var properties (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, and ProgressRate are all float. Write 0.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 CurrentProgress snaps CurrentProgress down 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 must set Device.ProgressState = progress_device_state.Progress for ProgressRate to take effect.

Build your own lesson with progress_based_mesh_device

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →