Reference Devices compiles

tracker_device: Quest Objectives That Actually Track Progress

The tracker_device is Fortnite Creative's all-in-one objective system: it shows a titled progress bar on the HUD, counts toward a target, fires an event when a player finishes, and can even persist progress across sessions. Whether you're building a wave-survival mode that counts eliminated guards, a scavenger hunt that tracks collected items, or a co-op quest with shared progress, the tracker_device is the right tool. This article walks through every major API call with real, compilable Verse c

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

Overview

The tracker_device solves a common island-design problem: how do I show players what they need to do and how far along they are? Placing a tracker in your level gives you:

  • A HUD widget (title + description + progress bar) that appears automatically when the device is assigned to a player.
  • A target value (0–10 000) that, when reached, fires CompleteEvent and can trigger downstream devices.
  • Verse API methods to assign/remove the tracker, increment or set the value, query state, and even persist progress between sessions.

When to reach for it:

  • Eliminate N guards → tracker counts eliminations.
  • Collect N items → tracker counts pickups.
  • Shared team objective → set Sharing to Team or All in device settings.
  • Persistent daily quest → enable Use Persistence and call Save/Load.

API Reference

tracker_device

Allows creation and HUD tracking of custom objectives for agents to complete.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

tracker_device<public> := class<concrete><final>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
CompleteEvent CompleteEvent<public>:listenable(agent) Signaled when the tracked value reaches GetTarget for an agent. Sends the agent that reached GetTarget for their tracked value.

Methods (call these to make the device act):

Method Signature Description
Assign Assign<public>(Agent:agent):void Assigns the device to Agent (and any agents sharing progress).
AssignToAll AssignToAll<public>():void Assigns this device to all valid agents.
Remove Remove<public>(Agent:agent):void Removes this device from Agent (and any agents sharing progress).
RemoveFromAll RemoveFromAll<public>():void Removes this device from all valid agents.
Complete Complete<public>(Agent:agent):void The objective immediately completes.
Increment Increment<public>(Agent:agent):void Increases the tracked value by Amount to Change on Received Signal for Agent.
Decrement Decrement<public>(Agent:agent):void Decrease the tracked value by Amount to Change on Received Signal for Agent.
IncreaseTargetValue IncreaseTargetValue<public>(Agent:agent):void Increases the target value for Agent by 1.
DecreaseTargetValue DecreaseTargetValue<public>(Agent:agent):void Decreases the target value for Agent by 1.
Save Save<public>(Agent:agent):void Saves tracked progress for Agent. Only valid if Use Persistence is set to Use.
Load Load<public>(Agent:agent):void Loads tracked progress for Agent. Only valid if Use Persistence is set to Use.
LoadForAll LoadForAll<public>():void Loads tracked progress for all valid agents. Only valid if Use Persistence is set to Use.
ClearPersistence ClearPersistence<public>(Agent:agent):void Clears tracked progress for Agent. Only valid if Use Persistence is set to Use.
SetDescriptionText SetDescriptionText<public>(Text:message):void Sets a description for the tracker_device, which is displayed if Show on HUD is enabled. Text has a 64 character limit.
SetTarget SetTarget<public>(TargetValue:int):void Sets the target value that must be achieved in order for CompleteEvent to trigger. Clamped to 0 <= TargetValue <= 10000.
GetTarget GetTarget<public>()<transacts>:int Returns the target value that must be achieved in order for CompleteEvent to trigger. Clamped to 0 <= GetTarget <= 10000.
SetValue SetValue<public>(Value:int):void Sets the current tracked value for the device for all active players.
SetValue SetValue<public>(TeamIndex:int, Value:int):void Sets the current tracked value for the device for the Team at the TeamIndex. If Sharing is set to Individual, this will set the value for all team members. If Sharing is set to All, this will set the value for all players.
SetValue SetValue<public>(Agent:agent, Value:int):void Sets the current tracked value for the device for a specific 'Agent'. If Sharing is set to Team, this will set the value for their team. If Sharing is set to All, this will set the value for everyone.
GetValue GetValue<public>()<transacts>:int Returns the current total tracked value for all players.
GetValue GetValue<public>(TeamIndex:int)<transacts>:int Returns the current total tracked value for the team at TeamIndex.
GetValue GetValue<public>(Agent:agent)<transacts>:int Returns the current tracked value for Agent.
SetTitleText SetTitleText<public>(Text:message):void Sets the title for the tracker_device, which is displayed if Show on HUD is enabled. Text has a 32 character limit.
IsActive IsActive<public>(Agent:agent)<transacts><decides>:void Is true if Agent currently has the tracker active.
HasReachedTarget HasReachedTarget<public>(Agent:agent)<transacts><decides>:void Is true if Agent has reached the TargetValue for the tracker.
GetActiveAgents GetActiveAgents<public>()<reads>:[]agent Returns an array of agents that currently have this tracker active.

Walkthrough

Scenario: Stronghold Defense — Eliminate 5 Guards to Win

A player enters the island, the tracker is assigned to them, and every time they eliminate a guard (signaled by a trigger_device wired to each guard spawner's elimination output) the tracker increments. When the count hits 5 the tracker completes, the vault door unlocks, and a congratulations message appears on the HUD.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }

# Helper so we can pass localized text to SetTitleText / SetDescriptionText
ElimTitle<localizes>(S : string) : message = "{S}"
ElimDesc<localizes>(S : string) : message = "{S}"

stronghold_tracker_manager := class(creative_device):

    # ── Editable device references ────────────────────────────────────────
    @editable
    GuardEliminatedTrigger : trigger_device = trigger_device{}
    # This trigger fires once per guard eliminated (wire it in the editor)

    @editable
    VaultDoorLock : lock_device = lock_device{}
    # The vault door that unlocks when the objective completes

    @editable
    ObjectiveTracker : tracker_device = tracker_device{}
    # The tracker_device placed in the level

    # ── Lifecycle ─────────────────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # 1. Set the HUD title and description (both take `message` values)
        ObjectiveTracker.SetTitleText(ElimTitle("Stronghold Defense"))
        ObjectiveTracker.SetDescriptionText(ElimDesc("Eliminate the guards!"))

        # 2. Set the target: 5 guards must be eliminated
        ObjectiveTracker.SetTarget(5)

        # 3. Assign the tracker to every player currently in the game
        ObjectiveTracker.AssignToAll()

        # 4. React when a guard is eliminated
        GuardEliminatedTrigger.TriggeredEvent.Subscribe(OnGuardEliminated)

        # 5. React when the tracker reaches its target
        ObjectiveTracker.CompleteEvent.Subscribe(OnObjectiveComplete)

    # ── Event handlers ────────────────────────────────────────────────────

    # Called each time the guard-eliminated trigger fires.
    # TriggeredEvent sends ?agent, so we unwrap it before using it.
    OnGuardEliminated(TriggeringAgent : ?agent) : void =
        if (A := TriggeringAgent?):
            # Increment uses the device's "Amount to Change on Received Signal"
            # setting (default 1), so each call adds 1 to that agent's count.
            ObjectiveTracker.Increment(A)

    # Called when an agent's tracked value reaches the target (5).
    # CompleteEvent sends a plain `agent` (not optional).
    OnObjectiveComplete(CompletingAgent : agent) : void =
        # Unlock the vault door for everyone
        VaultDoorLock.Unlock(CompletingAgent)

        # Update the HUD description to celebrate
        ObjectiveTracker.SetDescriptionText(ElimDesc("Stronghold cleared!"))

        # Remove the tracker from all players so the bar disappears
        ObjectiveTracker.RemoveFromAll()

Line-by-line explanation:

Lines What's happening
SetTitleText / SetDescriptionText Push localized strings to the HUD widget. Both take message, not string — see the helper functions at the top.
SetTarget(5) Overrides the editor value at runtime. The tracker fires CompleteEvent when any assigned agent's value hits 5.
AssignToAll() Activates the tracker for every player in the session so the HUD bar appears immediately.
GuardEliminatedTrigger.TriggeredEvent.Subscribe(OnGuardEliminated) Wires the guard-elimination trigger to our handler.
ObjectiveTracker.CompleteEvent.Subscribe(OnObjectiveComplete) Wires the tracker's built-in completion event.
Increment(A) Adds one step to that agent's progress. The device's Amount to Change on Received Signal property controls the step size.
VaultDoorLock.Unlock(CompletingAgent) Opens the vault door — the payoff for finishing the objective.
RemoveFromAll() Clears the HUD bar once the quest is done.

Common patterns

Pattern 1 — Query progress and conditionally boost the target

After a reinforcement wave spawns, raise the target by 3 so players have more guards to eliminate. Use GetValue and GetTarget to log current state, and IncreaseTargetValue to bump the target per-agent.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }

reinforcement_wave_handler := class(creative_device):

    @editable
    ReinforcementTrigger : trigger_device = trigger_device{}
    # Fires when the reinforcement wave spawns

    @editable
    ObjectiveTracker : tracker_device = tracker_device{}

    OnBegin<override>()<suspends> : void =
        ReinforcementTrigger.TriggeredEvent.Subscribe(OnReinforcementsArrived)

    OnReinforcementsArrived(TriggeringAgent : ?agent) : void =
        # Get every agent currently assigned to this tracker
        ActiveAgents := ObjectiveTracker.GetActiveAgents()

        for (A : ActiveAgents):
            # Only raise the bar for agents who haven't finished yet
            if (not ObjectiveTracker.HasReachedTarget[A]):
                # Current target before we change it
                CurrentTarget := ObjectiveTracker.GetTarget()
                # Current value for this specific agent
                CurrentValue  := ObjectiveTracker.GetValue(A)

                # Raise the target by 3 (call IncreaseTargetValue three times)
                ObjectiveTracker.IncreaseTargetValue(A)
                ObjectiveTracker.IncreaseTargetValue(A)
                ObjectiveTracker.IncreaseTargetValue(A)

Key calls: GetActiveAgents(), HasReachedTarget[] (failable — used in if (not …)), GetTarget(), GetValue(Agent), IncreaseTargetValue(Agent).


Pattern 2 — Persistent daily quest: save and load progress

Enable Use Persistence on the tracker in the editor, then call Load when a player joins and Save whenever they make progress. Progress survives across sessions.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }

DailyQuestTitle<localizes>(S : string) : message = "{S}"
DailyQuestDesc<localizes>(S : string)  : message = "{S}"

daily_quest_manager := class(creative_device):

    @editable
    QuestTracker : tracker_device = tracker_device{}
    # Must have "Use Persistence" = Use in device settings

    @editable
    CollectTrigger : trigger_device = trigger_device{}
    # Fires when the player collects a quest item

    OnBegin<override>()<suspends> : void =
        QuestTracker.SetTitleText(DailyQuestTitle("Daily Quest"))
        QuestTracker.SetDescriptionText(DailyQuestDesc("Collect 10 crystals"))
        QuestTracker.SetTarget(10)

        # Load saved progress for ALL players who are already in the session
        QuestTracker.LoadForAll()

        CollectTrigger.TriggeredEvent.Subscribe(OnItemCollected)
        QuestTracker.CompleteEvent.Subscribe(OnQuestComplete)

    OnItemCollected(TriggeringAgent : ?agent) : void =
        if (A := TriggeringAgent?):
            if (QuestTracker.IsActive[A]):
                QuestTracker.Increment(A)
                # Persist the new value immediately
                QuestTracker.Save(A)

    OnQuestComplete(A : agent) : void =
        # Clear persistence so the quest resets tomorrow
        QuestTracker.ClearPersistence(A)
        QuestTracker.Remove(A)

Key calls: LoadForAll(), IsActive[] (failable — used inside if), Increment(Agent), Save(Agent), ClearPersistence(Agent), Remove(Agent).


Pattern 3 — Force-set a value and immediately complete for a specific agent

Sometimes a power-up should instantly finish the objective for one player (e.g., they found the golden idol). Use SetValue(Agent, Value) to jump straight to the target, or call Complete(Agent) to skip counting entirely.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }

GoldenIdolTitle<localizes>(S : string) : message = "{S}"

golden_idol_handler := class(creative_device):

    @editable
    IdolPickupTrigger : trigger_device = trigger_device{}
    # Fires when the golden idol is picked up

    @editable
    QuestTracker : tracker_device = tracker_device{}

    OnBegin<override>()<suspends> : void =
        QuestTracker.SetTitleText(GoldenIdolTitle("Find the Idol"))
        QuestTracker.SetTarget(1)
        QuestTracker.AssignToAll()
        IdolPickupTrigger.TriggeredEvent.Subscribe(OnIdolPickedUp)

    OnIdolPickedUp(TriggeringAgent : ?agent) : void =
        if (A := TriggeringAgent?):
            # Option A: jump the value to the target
            QuestTracker.SetValue(A, QuestTracker.GetTarget())

            # Option B (alternative): skip counting entirely
            # QuestTracker.Complete(A)

            # Decrement the other team's tracker value as a penalty example
            # (demonstrates Decrement)
            QuestTracker.Decrement(A)

Key calls: SetValue(Agent, int), GetTarget(), Complete(Agent), Decrement(Agent).


Gotchas

1. message vs string — the #1 compile error

SetTitleText and SetDescriptionText require a message value, not a plain string. There is no StringToMessage function. Always declare a localizer helper:

MyTitle<localizes>(S : string) : message = "{S}"

Then call ObjectiveTracker.SetTitleText(MyTitle("Eliminate Guards")). Passing a raw string literal will not compile.

2. CompleteEvent sends agent, not ?agent

CompleteEvent : listenable(agent) — the handler receives a plain, already-unwrapped agent. Do not write if (A := Agent?) here; just use Agent directly. Contrast this with trigger_device.TriggeredEvent which sends ?agent and does need unwrapping.

3. IsActive[] and HasReachedTarget[] are failable — use if

Both methods carry <decides>, meaning they can fail. Always call them inside an if expression:

if (ObjectiveTracker.IsActive[A]):
    # safe to proceed

Calling them outside an if is a compile error.

4. Persistence methods are no-ops without the editor setting

Save, Load, LoadForAll, and ClearPersistence silently do nothing unless the tracker's Use Persistence property is set to Use in the device's User Options panel. Enable it in the editor first.

5. SetTarget is global, IncreaseTargetValue / DecreaseTargetValue are per-agent

SetTarget(int) changes the target for all agents at once and is clamped to 0–10 000. IncreaseTargetValue(Agent) / DecreaseTargetValue(Agent) adjust the target by 1 for a single agent. Mix them carefully or you'll accidentally reset everyone's bar.

6. AssignToAll only covers players present at call time

If a player joins mid-game, they won't automatically receive the tracker. Subscribe to a join event (e.g., from a player_spawner_device) and call Assign(Agent) for late joiners.

7. intfloat — no auto-conversion

Verse never silently converts between int and float. SetTarget and SetValue take int; if you compute a value as float you must convert explicitly (e.g., Int(FloatValue) if the built-in is available, or redesign the math to stay in int).

Device Settings & Options

The tracker_device User Options panel in the UEFN editor — every setting you can tune.

Tracker Device settings and options panel in the UEFN editor — Stat to Track, Target Value, Starting Value, Assign on Game Start, Assign when Joining in Progress, Tracker Title, Description Text, HUD Widget
Tracker Device — User Options in the UEFN editor: Stat to Track, Target Value, Starting Value, Assign on Game Start, Assign when Joining in Progress, Tracker Title, Description Text, HUD Widget
⚙️ Settings on this device (8)

Setting names read from the panel above — may be partial; the screenshots are the source of truth.

Stat to Track
Target Value
Starting Value
Assign on Game Start
Assign when Joining in Progress
Tracker Title
Description Text
HUD Widget

Guides & scripts that use tracker_device

Step-by-step tutorials that put this object to work.

Build your own lesson with tracker_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 →