Overview
The analytics_device is a lightweight tracking device whose sole job is to log that a specific agent did something noteworthy at a point in time. Epic batches those submissions and delivers them to your Creator Portal dashboard once per day.
When should you reach for it?
- Funnel analysis — how many players reach Zone 2 vs Zone 3?
- Capture-the-flag / objective games — how often does each team score?
- Boss encounters — how many players survive the final wave?
- A/B testing — did players interact with the new chest spawn more than the old one?
The device has no visual presence in-game; it is purely a data sink. You pair it with any device that produces an agent (trigger, capture area, elimination manager, etc.) and call Submit(Agent) to record the event.
API Reference
analytics_device
Used to track
agentevents used to generate analytics.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
analytics_device<public> := class<concrete><final>(creative_device_base):
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
Submit |
Submit<public>(Agent:agent):void |
Submits an event for Agent. |
Walkthrough
Scenario: Checkpoint Tracker
You have a three-zone obstacle course. A trigger_device sits at the entrance of each zone. Every time a player steps on a trigger, you want to record which zone they reached. One analytics_device per zone lets the Creator Portal show you a funnel: Zone 1 entries vs Zone 2 vs Zone 3.
The script below wires three triggers to three analytics devices. When a player steps on Trigger 1, AnalyticsZone1.Submit(Agent) fires. The device is also disabled during a brief setup countdown so no phantom events slip through before the round starts.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# Place this Verse device in your UEFN scene, then assign the
# six @editable fields in the Details panel.
checkpoint_analytics_manager := class(creative_device):
# --- Editable device references ---
@editable
TriggerZone1 : trigger_device = trigger_device{}
@editable
TriggerZone2 : trigger_device = trigger_device{}
@editable
TriggerZone3 : trigger_device = trigger_device{}
@editable
AnalyticsZone1 : analytics_device = analytics_device{}
@editable
AnalyticsZone2 : analytics_device = analytics_device{}
@editable
AnalyticsZone3 : analytics_device = analytics_device{}
# --- Lifecycle ---
OnBegin<override>()<suspends> : void =
# Disable all analytics devices during the pre-round countdown
# so test-walk-throughs before the match starts aren't recorded.
AnalyticsZone1.Disable()
AnalyticsZone2.Disable()
AnalyticsZone3.Disable()
# Wait 10 seconds for the round to officially begin, then enable.
Sleep(10.0)
AnalyticsZone1.Enable()
AnalyticsZone2.Enable()
AnalyticsZone3.Enable()
# Subscribe each trigger to its matching handler.
TriggerZone1.TriggeredEvent.Subscribe(OnZone1Entered)
TriggerZone2.TriggeredEvent.Subscribe(OnZone2Entered)
TriggerZone3.TriggeredEvent.Subscribe(OnZone3Entered)
# --- Event handlers ---
# trigger_device.TriggeredEvent sends ?agent, so we unwrap it.
OnZone1Entered(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
AnalyticsZone1.Submit(Agent)
OnZone2Entered(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
AnalyticsZone2.Submit(Agent)
OnZone3Entered(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
AnalyticsZone3.Submit(Agent)
Line-by-line explanation
| Lines | What's happening |
|---|---|
@editable fields |
Expose the six devices to the Details panel so you can drag-assign them without touching code. |
Disable() × 3 |
Prevents events from being counted during the pre-game lobby walk-around. |
Sleep(10.0) |
Suspends this coroutine for 10 seconds — the round start window. |
Enable() × 3 |
Re-arms all three analytics devices the moment the round begins. |
Subscribe(OnZone1Entered) |
Registers the handler; it will be called every time any player steps on that trigger. |
if (Agent := MaybeAgent?) |
Safely unwraps the optional agent. If the trigger fired without a player (impossible here, but safe practice), nothing is submitted. |
Submit(Agent) |
The core call — records that this agent reached this zone right now. |
Common patterns
Pattern 1 — Submit on elimination (boss kill tracker)
An npc_spawner_device fires EliminatedEvent with a device_ai_interaction_result. Extract the eliminating agent and submit to an analytics device so you can see how many players beat your boss.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
boss_kill_tracker := class(creative_device):
@editable
BossSpawner : npc_spawner_device = npc_spawner_device{}
@editable
BossKillAnalytics : analytics_device = analytics_device{}
OnBegin<override>()<suspends> : void =
BossKillAnalytics.Enable()
BossSpawner.EliminatedEvent.Subscribe(OnBossEliminated)
# EliminatedEvent sends device_ai_interaction_result
OnBossEliminated(Result : device_ai_interaction_result) : void =
# GetInstigator() returns ?agent — unwrap before submitting
if (Killer := Result.GetInstigator[]?):
BossKillAnalytics.Submit(Killer)
What this covers: Enable() + Submit() in a non-trigger context, showing that any device event that yields an agent can feed analytics.
Pattern 2 — Disable analytics mid-match (pause window)
Some game modes have a halftime break or a grace period where player movement shouldn't count. Toggle the analytics device off for that window, then back on.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
halftime_analytics_guard := class(creative_device):
@editable
ObjectiveCaptureAnalytics : analytics_device = analytics_device{}
# How long the halftime pause lasts (seconds)
@editable
HalftimeDuration : float = 30.0
OnBegin<override>()<suspends> : void =
# Analytics active for first half
ObjectiveCaptureAnalytics.Enable()
# Simulate a match half of 3 minutes
Sleep(180.0)
# Halftime — stop recording
ObjectiveCaptureAnalytics.Disable()
Sleep(HalftimeDuration)
# Second half — resume recording
ObjectiveCaptureAnalytics.Enable()
# Match ends after another 3 minutes
Sleep(180.0)
ObjectiveCaptureAnalytics.Disable()
What this covers: The full Enable() / Disable() lifecycle, showing how to bracket a recording window precisely around your game phases.
Pattern 3 — Multiple analytics devices, one trigger
Sometimes one player action is meaningful to multiple funnels (e.g., a player who reaches the final zone also counts toward a "survived wave 5" metric). Submit to several analytics devices from a single handler.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
multi_funnel_tracker := class(creative_device):
@editable
FinalZoneTrigger : trigger_device = trigger_device{}
# Funnel: how many players reached the end?
@editable
ReachedEndAnalytics : analytics_device = analytics_device{}
# Separate funnel: how many players survived all five waves?
@editable
SurvivedAllWavesAnalytics : analytics_device = analytics_device{}
OnBegin<override>()<suspends> : void =
ReachedEndAnalytics.Enable()
SurvivedAllWavesAnalytics.Enable()
FinalZoneTrigger.TriggeredEvent.Subscribe(OnFinalZoneReached)
OnFinalZoneReached(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
# One action, two data points
ReachedEndAnalytics.Submit(Agent)
SurvivedAllWavesAnalytics.Submit(Agent)
What this covers: Calling Submit() on multiple analytics devices from one handler — a common real-world pattern for cross-cutting metrics.
Gotchas
1. Submit requires a live, unwrapped agent — not ?agent
trigger_device.TriggeredEvent and many other device events hand you ?agent (an optional). If you pass the optional directly to Submit, it won't compile. Always unwrap first:
# WRONG — won't compile
AnalyticsDevice.Submit(MaybeAgent) # MaybeAgent is ?agent, not agent
# RIGHT
if (Agent := MaybeAgent?):
AnalyticsDevice.Submit(Agent)
2. Events submitted while the device is Disabled are silently dropped
Disable() doesn't queue events for later — they are simply not recorded. If you call Submit(Agent) on a disabled analytics device, nothing is logged and no error is raised. Always call Enable() before the window you care about.
3. Data appears in Creator Portal with a ~24-hour delay
Analytics submissions are batched and delivered once per day. Don't expect real-time data. Use the Creator Portal's analytics dashboard (not in-game) to review results after your playtest.
4. The device must be placed in the scene AND assigned in the Details panel
Declaring @editable MyAnalytics : analytics_device = analytics_device{} gives you a default-constructed device if you forget to assign it in the Details panel. That default device is not the one you placed in the world, so submissions go nowhere. Always verify the field is wired in the Details panel before playtesting.
5. One analytics device = one named event in the Creator Portal
The Creator Portal identifies events by the name you give the analytics device in the UEFN Outliner. Use descriptive names like Analytics_Zone1_Entry rather than the default analytics_device. You cannot rename them after data has been collected without breaking your historical series.