We use cookies only to manage your session — no third-party tracking or advertising. Learn more in our Privacy Policy.

RoLearn

Unlock Pro features

DISCOVER
Back to Guidance

Unity Integration Guide: RoLearn Multiplatform SDK

Guide
May 24, 2026
8 min
RoLearn SDK Team
SDK
Unity
Integration
C#

This guide walks through integrating the RoLearn SDK into a Unity project beyond the bare minimum. The install page covers UPM package setup; this page covers the things that actually matter in a shipping Unity title — where to put Init(), how to coexist with your existing analytics, persistent-buffer behavior across app suspension, and the four Unity-specific gotchas we see customers hit most.

Installation in 60 seconds

Two paths, depending on how your project pulls packages:

  1. UPM via git URL (recommended). Open Window → Package Manager → "+" → "Add package from git URL", paste the repository URL from the downloads page. UPM pins to the tag you select; bump the tag manually to upgrade.
  2. Manual import. Download the .unitypackage from the downloads page, drag it into Unity. Pulls in RoLearnSDK.cs and the example bootstrap. No dependencies beyond what Unity ships.

The SDK targets Unity 2021.3 LTS or newer, .NET Standard 2.1, and works on the IL2CPP backend. Mono backend also works but is increasingly rare in 2026 builds.

The bootstrap pattern

RoLearnSDK is a static class, not a MonoBehaviour. You do not drop it in the scene; you call Init() once during app bootstrap, then call Track() from anywhere for the rest of the session.

The canonical bootstrap lives in a MonoBehaviour attached to a DontDestroyOnLoad object in your first scene:

using UnityEngine;
using System.Collections.Generic;
using RoLearn;

public class RoLearnBootstrap : MonoBehaviour
{
    [SerializeField] private string apiKey;     // injected from build config
    [SerializeField] private string gameId;     // typically Application.identifier

    void Awake()
    {
        DontDestroyOnLoad(gameObject);

        RoLearnSDK.Init(new RoLearnConfig {
            ApiKey      = apiKey,
            Endpoint    = "https://rolearn.dev/api/sdk/events",
            GameId      = string.IsNullOrEmpty(gameId)
                            ? Application.identifier
                            : gameId,
            Platform    = "unity",
            PersistOnDisk = true,    // survives app kill
        });
    }
}

Three intentional choices in that template:

  • API key from serialized field, not hardcoded.Use a build-config asset (ScriptableObject) populated per build target rather than committing the key to source. The key has team and (optionally) per-game scope, but it is still a secret relative to your players.
  • DontDestroyOnLoad. The bootstrap GameObject persists across scene loads so the SDK survives scene changes without re-initializing.
  • PersistOnDisk = true. Buffers unsent events to Application.persistentDataPath so you do not lose telemetry if the app is killed before the next flush. Default is on; explicit here for clarity.

Sending your first custom event

After Init() returns, the SDK is ready. Track an event from anywhere in your codebase:

RoLearnSDK.Track("level_complete", new Dictionary<string, object> {
    ["level"]    = 5,
    ["score"]    = 1840,
    ["duration"] = 92,
    ["result"]   = "win",
}, playerId: PlayerProfile.Id);

Three things to know about the signature:

  • The event type string is the discriminator on the server side. Use snake_case to stay consistent with auto-emitted events (session_start, session_end, purchase).
  • The payload is a Dictionary<string, object>. The SDK serializes it to JSON before send; primitive types, strings, and arrays of those work. Nested dictionaries also work for one level. Avoid sending large objects — keep per-event payloads under 1 KB serialized.
  • playerId is the third optional parameter. If your game has a stable user ID (your own backend, Steam, Google Play, Apple GameCenter), pass it here so retention cohorts can stitch sessions together. If you only have a session-scoped ID, omit it — the SDK will still emit the event with an anonymous session_id.

What the SDK auto-instruments

Out of the box (no code from you beyond Init()), the Unity SDK emits:

  • session_start when Init() returns. Includes country resolved from RegionInfo(CultureInfo.CurrentCulture.Name).
  • session_end on Application.quitting with session duration.
  • performance snapshots periodically (FPS p5/p50/p95 from the Unity main thread, memory from Profiler.GetTotalAllocatedMemoryLong, device model).
  • crash events when an unhandled exception fires via Application.logMessageReceived.

That auto-instrumentation alone populates DAU, geo distribution, session-length cohorts, FPS percentiles, and crash signature grouping on the SDK dashboard. Most studios run on it for the first month while they design their custom event vocabulary.

Purchase tracking — IAP, ads, Steam, the works

Unity doesn't have a single "marketplace" abstraction the way Roblox does, so purchase tracking is explicit. The pattern:

// After a successful IAP completes (UnityEngine.Purchasing)
public void OnPurchaseComplete(PurchaseEventArgs args)
{
    RoLearnSDK.Purchase(new PurchaseInfo {
        Sku       = args.purchasedProduct.definition.id,
        Name      = args.purchasedProduct.metadata.localizedTitle,
        Currency  = args.purchasedProduct.metadata.isoCurrencyCode,
        Price     = (double)args.purchasedProduct.metadata.localizedPrice,
        Quantity  = 1,
        Source    = "unity_iap",
    });
}

Use the same pattern for Steam purchases (in theOnMicroTxnAuthorizationResponse callback), ad rewards (via TrackAdImpression), and any custom currency exchange flows. The server-side schema validates currency codes and rejects unknown SKUs that look generated.

Lifecycle hooks that matter on mobile

Mobile platforms suspend Unity apps without firing Application.quitting. The SDK handles this gracefully (it persists buffered events to disk via PersistOnDisk and re-emits them next launch), but you can force a synchronous flush on suspend if you need an event to land before the app backgrounds:

void OnApplicationPause(bool pause)
{
    if (pause) {
        RoLearnSDK.Flush();   // blocks until current batch sends or 2s timeout
    }
}

Use Flush() sparingly. The SDK's normal flush cadence (every 10 seconds, batches of 50) is tuned for low overhead; a forced synchronous flush on every pause makes a meaningful battery-life difference if you have a busy event stream.

Coexisting with your existing analytics

Most teams adopting RoLearn already have Unity Analytics, Firebase, Amplitude, or a homegrown pipeline. The SDK is designed to coexist:

  • RoLearn doesn't touch global Unity state. No static event interception, no replacement of Debug.unityLogger, no hijacked HTTP. You can run two analytics SDKs in parallel without behavior interaction.
  • Event-stream duplication is intentional. Forward the events you want to both systems by wrapping your call sites in a small helper, not by re-broadcasting from a single SDK to the other.
  • Don't send the same event to both with different shapes. Pick one canonical event name + payload schema per logical event and send the same JSON to both. Divergent shapes are the #1 source of confusing dashboards when teams run side-by-side analytics.

Common Unity-specific gotchas

  1. IL2CPP stripping. If you build with managed stripping at "High", IL2CPP may strip parts of the SDK's JSON serialization. Add link.xml entries to preserve the SDK assembly, or drop stripping to "Low". The downloads page ships a recommended link.xml snippet.
  2. Application.identifier changes between dev and prod. If you use Application.identifier as GameId, your dev builds emit under a different ID than your prod builds. That's usually what you want (separate environments), but verify which ID is configured in your SDK key's per-experience binding.
  3. Threading. All SDK methods are safe to call from any thread. The internal batcher runs on a worker thread; your callback in before_send runs on the calling thread. Don't touch UnityEngine APIs inside before_send — they're main-thread-only and will throw if called from the worker.
  4. Mobile data privacy. Both Apple's ATT and Android's data safety form require disclosure of any analytics SDK that collects device identifiers. The Unity SDK collects device model + OS for performance bucketing; document this on your store listing's data-practices disclosure. The GDPR compliance guide covers the legal-side framing.

Verifying the integration

Build for your target platform, launch the app, and verify in the RoLearn web app under SDK → Events. You should see your session_start within seconds, then your custom events as they fire. Three diagnostic steps if events are not arriving:

  1. Confirm Init() returned without throwing. Check your Unity logcat / Xcode console for [RoLearn]-tagged messages — the SDK logs init state, batch sends, and HTTP errors.
  2. Confirm the API key is correct and matches the team your dashboard is logged into. Keys are team-scoped; an event sent with a key from team A will not appear under team B.
  3. Confirm Endpoint is the right URL. The default in this guide (https://rolearn.dev/api/sdk/events) is the production endpoint. Self-hosted or staging environments use a different host.

Where to go next