Reference Devices compiles

analytics_device: Track What Players Actually Do

The `analytics_device` lets you record meaningful player interactions — checkpoint crossings, flag captures, boss kills — and surface that data in the Creator Portal. Instead of guessing whether players reach your final zone, you get hard numbers. Drop one in your scene, wire it to Verse, and call `Submit` every time something important happens.

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

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 agent events 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.

Guides & scripts that use analytics_device

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

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