Unity SDK
Официальный плагин SpyMagic для Unity. Ставится как обычный пакет (Asset Store / Package Manager), настраивается в
Edit → Project Settings → SpyMagic и с первого кадра начинает отправлять события:
очередь с сохранением между запусками, батчи, повторы при обрывах связи и типизированные методы под все встроенные события аналитики.
Требования
| параметр |
значение |
| Unity |
2021.3 LTS и новее (2022.3, 6000.x) |
| Платформы |
WebGL, Android, iOS, Windows / macOS / Linux Standalone |
| Зависимости |
нет (ни Newtonsoft.Json, ни других пакетов), только UnityEngine |
| Scripting backend |
Mono и IL2CPP |
| Размер |
~10 файлов C#, один .jslib для WebGL |
Скачать
Публикация в Unity Asset Store в процессе. После публикации пакет можно будет добавить в проект прямо из окна Package Manager (My Assets) — ссылка появится здесь.
Установка
Вариант 1 — Package Manager, tarball (рекомендуем)
1
Скачайте com.spymagic.sdk-1.0.0.tgz и положите его рядом с проектом (например, в папку Packages/).
2
В Unity откройте Window → Package Manager, нажмите + → Add package from tarball... и выберите файл.
3
Пакет появится в списке как SpyMagic Analytics SDK. Во вкладке Samples можно импортировать примеры.
Вариант 2 — Package Manager, из папки
1
Распакуйте zip. Внутри одна папка com.spymagic.sdk.
2
+ → Add package from disk... и укажите com.spymagic.sdk/package.json. Либо просто скопируйте папку в Packages/ вашего проекта — Unity подхватит её как embedded package.
Вариант 3 — manifest.json
"dependencies": {
"com.spymagic.sdk": "file:../Packages/com.spymagic.sdk",
...
}
Настройка
1
Откройте Edit → Project Settings → SpyMagic (или Tools → SpyMagic → Settings). При первом открытии плагин сам создаст ассет настроек Assets/Resources/SpyMagicSettings.asset.
2
Вставьте в поле
Token ключ проекта. Ключ видит администратор компании в разделе
Настройки → Игры, кнопка «показать» рядом с иконкой ключа.
3
Проверьте блок
Platform: по умолчанию Android → Google Play (2), iOS → Apple Store (5), WebGL и десктоп → www (1). Если билд один и живёт на одной площадке — поставьте
Platform Override. Если WebGL-билд выкладывается на несколько порталов, площадку лучше выставлять в рантайме через
SpyMagicSDK.SetPlatform(...), см.
таблицу ID платформ.
4
Остальное оставьте по умолчанию: Auto Init и Auto Send Init включены — SDK стартует до загрузки первой сцены и сразу шлёт init.
| поле |
по умолчанию |
описание |
| Token | — | ключ проекта, уходит в заголовке Mig-Token |
| Endpoint | https://spymagic.pro/track/ | адрес трекера, менять не нужно |
| Timeout Seconds | 5 | таймаут одного HTTP-запроса |
| Batch Interval | 30 | раз в сколько секунд отправлять очередь. 0 — каждое событие отдельным запросом (не рекомендуем) |
| Max Batch Size | 30 | отправить сразу, когда в очереди накопилось столько событий. Пока sid ≤ 100 (начало сессии) — порог 10, чтобы первые минуты игрока были видны быстрее |
| Max Queue Size | 200 | предел офлайн-очереди, самые старые события выбрасываются |
| Persist Queue | вкл | хранить неотправленное между запусками (PlayerPrefs) |
| Send Package Meta | вкл | добавлять к каждому батчу служебное событие package_send с числом событий |
| Auto Init | вкл | инициализация до первой сцены. Выключите, если хотите вызывать SpyMagicSDK.Init() сами |
| Auto Send Init | вкл | слать init при инициализации. На WebGL пропускается, если init уже отправила страница (см. раздел WebGL) |
| Track Focus | вкл | события focus_lost / focus_gain |
| Use Device Identifier | вкл | на Android / iOS uuid считается как uuid v5 от SystemInfo.deviceUniqueIdentifier, переустановка не плодит нового игрока |
| Platform Override / Mapping | см. выше | какой platform_id слать |
| Debug Log | выкл | писать диагностику в консоль Unity |
| Disable In Editor | выкл | ничего не слать из редактора |
Как это работает
uuid. Определяется один раз и хранится в PlayerPrefs. На WebGL сначала берётся
localStorage.uuid (его заводит наш JS-сниппет), на мобильных — uuid v5 от идентификатора устройства, иначе случайный uuid v4. Это тот же uuid, который описан в
API.
sid. Каждому событию SDK присваивает порядковый номер внутри сессии и unix-время, поэтому сервер восстанавливает порядок даже при плохой связи.
Очередь и батчи. Событие попадает в очередь. Очередь уходит одним POST-запросом каждые Batch Interval секунд, при достижении Max Batch Size, при forceSend, а также при потере фокуса, паузе и выходе из приложения.
Повторы. Ответ 2xx — доставлено. Статус 0 (нет сети) или 5xx — события возвращаются в голову очереди и уйдут со следующим батчем. 4xx (неверный токен, битые данные) — батч выбрасывается, повтор бы не помог.
Персист. Неотправленное сохраняется раз в 2 секунды и при сворачивании; на следующем запуске хвост прошлой сессии уходит первым.
Тело запроса, которое формирует SDK (формат format = 1):
POST https://spymagic.pro/track/
Content-Type: application/json
Mig-Token: <token>
{
"format": 1,
"uuid": "b2ce6279-cb36-45a1-b26c-ad40d4004e44",
"user_id": 10455,
"platform": 2,
"events": [
{"sid": 0, "event": "init", "detail": {"platform": 2, "version": "1.4.0", "language": "ru", "device": {...}, "screen": {...}}, "time": 1757800000},
{"sid": 1, "event": "loading", "detail": {"step": 1}, "time": 1757800001},
{"sid": 2, "event": "hit", "detail": {"url": "lobby"}, "time": 1757800004},
{"sid": 3, "event": "package_send", "detail": {"events_num": 3}, "time": 1757800030}
]
}
Идентификация игрока
Пока игрок анонимен, события идут только с uuid. Как только ваш сервер выдал числовой id аккаунта, сообщите об этом SDK — иначе не будет ни retention, ни когорт, ни платежей по игрокам.
| метод |
когда вызывать |
SpyMagicSDK.Register(long userId, string userName = null) |
один раз в жизни игрока — аккаунт только что создан. Считается установкой в дашбордах |
SpyMagicSDK.Auth(long userId, Dictionary<string, object> extra = null) |
каждый последующий запуск, когда игрок вошёл в существующий аккаунт. SDK сам добавит платформу, версию, язык, устройство и install_id |
SpyMagicSDK.Logout() |
игрок вышел из аккаунта; очередь сразу отправляется |
SpyMagicSDK.UserSet(string key, object value) |
свойства профиля: name, mail, password (0/1), gender (0..2), sound и music (0..100) — см. Игроки |
SpyMagicSDK.SetUserId(long userId) |
если id известен без Register/Auth (редко). Register и Auth вызывают его сами |
user_id — целое число (в C# long). Если у вас строковые id, заведите числовой суррогат на сервере: аналитика хранит id как bigint.
Методы и события
Все методы статические, в пространстве имён
SpyMagic, класс
SpyMagicSDK. Каждый метод формирует событие с тем именем и
detail, которые ждут дашборды; подробности по каждому событию — в разделе
События.
| метод |
событие |
для чего |
| Техника |
SendInit(extra = null) | init | старт сессии; вызывается автоматически. Руками — только если выключили Auto Send Init |
Loading(int step, string state = null) | loading | этап загрузки билда 1..N, метрика time to interactive |
Hit(string url, props = null) | hit | смена сцены/экрана, уникальное имя сцены |
Click(float x, float y, string scene) | click | тепловые карты кликов |
Error(string text) | error | клиентская ошибка |
HttpRequest(url, method, status, timeSeconds) | http_request | запросы к вашему серверу — отчёт по пингу и лагам |
| автоматически | focus_lost / focus_gain | сворачивание и возврат в игру (Track Focus) |
| Прогресс — описание |
Levelup(int level) | levelup | уровень аккаунта |
Tutorial(int step), TutorialComplete() | tutorial, tutorial_complete | воронка туториала |
BattleResult(result, point, timeSeconds, location = 0, power = 0, isAuto = false, isSurrender = false) | battle_result | итог боя: result 1/0/-1, point — номер уровня |
LevelResult(...) | level_result | то же для игр с уровнями |
StoryPoint(int step) | story_point | точки сюжета |
NarrativeStart / NarrativeStep / NarrativeSkip / NarrativeFinish | narrative_* | катсцены и нарратив |
Dialogue(int id), DialogueSkip(int id, int step) | dialogue, dialogue_skip | диалоги |
Feature(string text) | feature | открытие фичи |
| Экономика — платежи, ресурсы |
Resource(Dictionary<string, object>) | resource | срез ресурсов на руках: ключ — хэш ресурса, значение — количество |
PurchaseInit(offerCode, priceCode, priceQuantity) | purchase_init | нажата кнопка покупки |
PurchaseMethod(int method, offerCode = null) | purchase_method | выбор способа оплаты (только свой эквайринг) |
Purchase(offerCode, priceCode, priceQuantity, extra = null) | purchase | платёж подтверждён стором/шлюзом. Очередь отправляется сразу |
PurchaseCanceled(transaction), PurchaseError(error, transaction) | purchase_canceled, purchase_error | отмена и ошибки оплаты |
VirtualPurchase(offerId, offerRewards, priceCurrency, priceCode, priceQuantity) | virtual_purchase | покупка за внутриигровую валюту |
| Реклама — описание |
RewardedInit(int sectionId = 0) | rewarded_init | запрошен rewarded-блок, sectionId — плейсмент |
RewardedReceived(int sectionId = 0) | rewarded_received | ролик досмотрен |
RewardedError(int sectionId = 0, string error = null) | rewarded_error | сеть не отдала рекламу |
| A/B и прочее — эксперименты |
Experiment(int experimentId, int groupId) | experiment | игрок попал в группу эксперимента. Эксперимент должен быть заведён в разделе |
Social(string key, object value) | social | социальные действия |
| Произвольные события |
Track(string eventName, Dictionary<string, object> detail = null, bool forceSend = false) | любое | кастомное событие; detail может содержать вложенные словари и списки |
Track(string eventName, string key, object value) | любое | кастомное событие с одним параметром |
TrackNow(string eventName, detail = null) | любое | отправить немедленно, минуя очередь (без повторов) |
| Управление |
Init() / Init(SpyMagicSettings) / Init(string token, SpyMagicPlatform platform) | — | ручная инициализация, если Auto Init выключен |
SetPlatform(SpyMagicPlatform) / SetPlatform(int) | — | сменить platform_id в рантайме (WebGL на разных порталах) |
SetEnabled(bool) | — | жёсткий выключатель: песочница, QA-сборки |
SetTrackEnabled(bool, string source = null) | — | мягкий выключатель, например по заголовку вашего сервера |
Flush() | — | отправить очередь сейчас |
OnLog (event) | — | диагностика: сбои доставки, переполнение очереди |
Uuid, UserId, Platform, QueueLength, IsInitialized | — | текущее состояние |
Пример
using System.Collections.Generic;
using SpyMagic;
using UnityEngine;
public class GameBootstrap : MonoBehaviour
{
private void Start()
{
// init уже ушёл автоматически до загрузки сцены
SpyMagicSDK.Loading(1);
// ваш сервер вернул аккаунт
Backend.SignIn(account =>
{
if (account.IsNew) SpyMagicSDK.Register(account.Id, account.Name);
else SpyMagicSDK.Auth(account.Id);
foreach (var exp in account.Experiments)
SpyMagicSDK.Experiment(exp.Id, exp.Group);
SpyMagicSDK.Loading(2);
SpyMagicSDK.Hit("lobby");
});
}
public void OnBattleFinished(bool isVictory, int level, int seconds, int power)
{
SpyMagicSDK.BattleResult(isVictory ? 1 : -1, level, seconds, location: 0, power: power);
SpyMagicSDK.Hit(isVictory ? "win" : "defeat");
}
public void OnLevelup(int level, int gold, int gems)
{
SpyMagicSDK.Levelup(level);
SpyMagicSDK.Resource(new Dictionary<string, object> { { "gold", gold }, { "gems", gems } });
}
public void OnBuyClicked(string offer, double price) => SpyMagicSDK.PurchaseInit(offer, "RUB", price);
public void OnPaymentConfirmed(string offer, double price) => SpyMagicSDK.Purchase(offer, "RUB", price);
public void OnBossUnlocked(int bossId)
{
SpyMagicSDK.Track("boss_unlocked", new Dictionary<string, object> { { "boss_id", bossId } });
}
}
Тот же сценарий целиком лежит в пакете: Package Manager → SpyMagic Analytics SDK → Samples → Basic Integration.
WebGL
init со страницы. Для веб-игр важно отправить init ещё до того, как загрузился движок — так считается честная скорость загрузки билда. В пакете есть сэмпл WebGL Template Snippet: скрипт spymagic-init.js, который вставляется в index.html вашего WebGL-шаблона перед загрузчиком Unity. Он генерирует uuid, кладёт его в localStorage, шлёт init и выставляет флаг window.spymagic.init_sent. SDK внутри билда подхватывает тот же uuid и второй init не отправляет.
Кросс-игровая кука. Запрос со страницы идёт с credentials: "include", поэтому аналитика может поставить общую куку mig_uid. Из UnityWebRequest такое невозможно, так что без сниппета кросс-игровое сопоставление игроков работать не будет.
Платформа. Один и тот же WebGL-билд может стоять на Яндекс.Играх, VK, CrazyGames. Определите портал (по домену, наличию YaGames / vkBridge / CrazyGames SDK) и вызовите SpyMagicSDK.SetPlatform(SpyMagicPlatform.YandexGames) как можно раньше. В сниппете для этого есть константа PLATFORM.
Блокировщики рекламы режут запросы к трекерам. SDK это переживает: события лягут в очередь и уйдут при следующем удачном флаше; после превышения Max Queue Size старые события отбрасываются.
<!-- index.html вашего WebGL-шаблона -->
<script>
// содержимое Samples/WebGL/spymagic-init.js, TOKEN / VERSION / PLATFORM заменить на свои
</script>
<script src="Build/{{{ LOADER_FILENAME }}}"></script>
Диагностика
Включите Debug Log в настройках — в консоли Unity появятся записи об отправленных батчах, ошибках HTTP и выброшенных событиях.
В окне Project Settings → SpyMagic в play mode есть кнопка Send test event (событие sdk_test) и живое состояние: uuid, user_id, platform, размер очереди.
Подпишитесь на SpyMagicSDK.OnLog, чтобы завести сбои доставки в собственный логгер. Отчётов не больше 20 за сессию, переполнение очереди репортится с прореживанием 1, 10, 100.
Проверить, что события доехали, можно в разделе
События проекта — батч уходит не позже чем через Batch Interval секунд (30 по умолчанию), либо вызовите
SpyMagicSDK.Flush().
SpyMagicSDK.OnLog += (message, data) =>
Debug.LogWarning("[SpyMagic] " + message + " " + SpyMagicJson.Serialize(data));
FAQ
| SDK многопоточный? |
Нет. Все вызовы — из главного потока Unity. Из фоновых потоков прокидывайте через свой диспетчер. |
| Что положить в detail? |
Строки, числа, bool, enum (уйдёт числом), Dictionary<string, object>, списки и массивы, null. Остальное сериализуется через ToString(). Свой JSON-сериализатор внутри, Newtonsoft не нужен. |
| Как не слать данные из QA-сборок? |
SpyMagicSDK.SetEnabled(false) сразу после инициализации, либо флаг Disable In Editor для редактора. Лучший вариант — отдельный проект в аналитике с другим токеном для тестового окружения. |
| Игрок переустановил игру — это новый игрок? |
На Android / iOS с включённым Use Device Identifier uuid считается от идентификатора устройства, так что нет. На WebGL uuid живёт в localStorage браузера. Связка с аккаунтом в любом случае идёт через Register / Auth. |
| Сервер ответил 401 |
Неверный токен или проект удалён. Проверьте поле Token — ключ целиком, с префиксом до точки. |
| Нужна другая платформа, которой нет в enum |
SpyMagicSDK.SetPlatform(int) принимает любой id из таблицы платформ. |
| Нет ли Defold-версии? |
Есть: Unity SDK повторяет её протокол и поведение очереди один в один. Статус публикации — на странице Defold. |