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
CompleteEventand 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. int ↔ float — 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).