Перейти к содержимому

MRAID для playable: что это и каркас под Unity Ads

MRAID для playable: Unity внедряет его сам. Каркас ready → viewableChange → старт и пауза → mraid.open по тапу на CTA, плюс запуск без MRAID.

Опубликовано
5 мин чтения
5 мин чтения
English
Українська
В статье
  1. MRAID на пальцах
  2. Порядок событий в каркасе
  3. Код: ready и viewableChange
  4. startPlayable и pausePlayable
  5. Звук
  6. mraid.open только по тапу на CTA
  7. Проверка: сборка без MRAID запускается в десктопном браузере
  8. Чего в доках нет

MRAID — что это: набор объектов и событий, через которые HTML-реклама общается с вебвью рекламного SDK. Playable узнаёт, что реклама готова и видна, и просит открыть стор. В Unity Ads вебвью внедряет MRAID сам, поэтому тег с MRAID-скриптом в сборку добавлять не нужно. Ниже каркас: старт по ready, пауза по viewableChange и mraid.open только по тапу на CTA после игры.

MRAID на пальцах

Расшифровка — Mobile Rich Media Ad Interface Definitions. По сути это договорённость: вебвью даёт рекламе объект mraid, а реклама пользуется им и не лезет в приложение сама. Через него playable делает четыре вещи:

  • mraid.getState() — узнаёт состояние, например loading;
  • событие ready — получает сигнал, что MRAID готов;
  • mraid.isViewable() и событие viewableChange — узнаёт, видна ли реклама сейчас;
  • mraid.open(url) — просит открыть ссылку, в нашем случае страницу в сторе.

Что говорят спецификации playable Unity: playable должен поддерживать MRAID 3.0, вебвью Unity Ads внедряет MRAID, а тег с MRAID-скриптом не нужен. Unity Ads поддерживает только полноэкранные MRAID 3.0 объявления. Не поддерживаются expand, resize, кастомные кнопки закрытия, sms, tel, calendar и storePicture. Другие сети могут требовать другую версию, сравнение собрано в статье требования к playable по сетям.

Порядок событий в каркасе

  1. Страница загрузилась. Игра не стартует: по документации Unity, контент, который идёт до того, как реклама видна, съедает первые секунды игрока и выглядит сломанным.
  2. MRAID готов (ready, либо getState() уже не loading). Настраиваем playable и подписываемся на viewableChange.
  3. Реклама видна. Стартуем, а если уже стартовали, возобновляем.
  4. Реклама не видна. Ставим на паузу и выключаем звук.
  5. Игрок поиграл, показан финальный экран с CTA. Тап на CTA вызывает mraid.open.

Код: ready и viewableChange

Этот блок адаптирован из примера в доках Unity. Мы перевели комментарии и добавили ветку без MRAID для десктопа. Сами startPlayable() и pausePlayable() Unity в примере только вызывает, не определяя.

// Адаптировано из примера в доках Unity (Playable specifications).
if (typeof mraid === "undefined") {
  // Наше добавление: сборка открыта в десктопном браузере, MRAID нет.
  startPlayable();
} else if (mraid.getState() === "loading") {
  // Ждём MRAID, потом настраиваем playable.
  mraid.addEventListener("ready", onReady);
} else {
  onReady();
}

function onReady() {
  // Пауза и возобновление по видимости рекламы.
  mraid.addEventListener("viewableChange", onViewableChange);
  // Стартуем, только если реклама уже видна.
  if (mraid.isViewable()) {
    startPlayable();
  }
}

function onViewableChange(viewable) {
  if (viewable) {
    startPlayable(); // старт или возобновление
  } else {
    pausePlayable(); // пауза и тишина
  }
}

Ставьте этот блок в конец скрипта, после объявлений состояния из следующего раздела: let-переменные должны существовать до первого вызова startPlayable().

startPlayable и pausePlayable

Это наша реализация, в доках Unity её нет. Идея: одно состояние paused, цикл анимации останавливается по-настоящему, а повторный startPlayable() при уже идущей игре ничего не делает. initGame, update, render, muteAudio и unmuteAudio — ваши функции.

let started = false;
let paused = true;
let raf = 0;
let last = 0;

function startPlayable() {
  if (!paused) return; // уже идёт
  paused = false;
  if (!started) {
    started = true;
    initGame(); // ваша сцена и обработчики ввода
  }
  unmuteAudio();
  raf = requestAnimationFrame(tick);
}

function pausePlayable() {
  paused = true;
  cancelAnimationFrame(raf);
  last = 0; // после паузы не копим огромный интервал между кадрами
  muteAudio();
}

function tick(now) {
  const dt = last ? now - last : 0;
  last = now;
  update(dt);
  render();
  raf = requestAnimationFrame(tick);
}

Если у вас есть обработчики ввода, начните их с if (paused) return;, иначе тап по скрытой рекламе будет влиять на игру.

Звук

Unity рекомендует Web Audio API как основной путь, а HTMLAudioElement только как запасной: он игнорирует mute-переключатель на iOS. Глушить и возвращать звук Unity советует из обработчика viewableChange, поэтому в каркасе это делают вызовы muteAudio() и unmuteAudio() внутри паузы и старта. Реализацию звука мы не приводим: проверьте её на реальном iPhone с включённым mute-переключателем и с блокировкой экрана.

mraid.open только по тапу на CTA

Правило Unity: стор открывается по тапу игрока на CTA после того, как он поиграл. Первый тап открыть стор не может, редиректы без действия игрока и обманные редиректы (когда стор открывает элемент геймплея) запрещены. Значит, кнопка с openStore живёт только на финальном экране. Блок ниже адаптирован из того же примера Unity, адреса сторов в нём — заглушки.

const STORE_URL = {
  ios: "https://apps.apple.com/app/idyourgame",
  android: "https://play.google.com/store/apps/details?id=yourgame",
};

function openStore() {
  const ua = navigator.userAgent || navigator.vendor;
  // Новые iPadOS в user agent пишут Macintosh: ищем только Android, остальное считаем iOS.
  const url = /android/i.test(ua) ? STORE_URL.android : STORE_URL.ios;
  if (typeof mraid === "undefined") window.open(url, "_blank"); // десктопный тест
  else mraid.open(url);
}

// ctaButton есть только на финальном экране, после игры.
ctaButton.addEventListener("click", () => {
  if (!paused) openStore();
});

Проверка: сборка без MRAID запускается в десктопном браузере

Best practices Unity просят убедиться, что финальная сборка запускается и без MRAID: так можно протестировать весь playable в десктопном браузере. В каркасе за это отвечает ветка typeof mraid === "undefined" и запасной window.open в openStore. Откройте собранный index.html в браузере и пройдите игру до CTA в мобильном разрешении.

Дальше нужен реальный телефон: Unity пишет, что десктопный браузер и симулятор не запускают playable в настоящем рекламном юните, и многие причины реджекта видны только на устройстве. Приложение Ad Testing открывается по QR-коду, который дашборд генерирует для каждого пака по кнопке Show QR code. Проверьте, что игра ставится на паузу и возобновляется по viewableChange, что CTA ведёт в правильный стор на iOS и Android и что поворот устройства её не ломает. Если локально всё работает, а в рекламе пустой экран, смотрите разбор пустого экрана. Полный список требований — в статье требования Unity Ads к playable.

Чего в доках нет

  • Определений startPlayable() и pausePlayable(): код выше наш, не Unity.
  • Какие версии SDK и вебвью внедряют MRAID.
  • Кодеков, частоты кадров и длительности аудио и видео.
  • Как именно модерация тестирует playable.

В DarkCore Ramet есть Playable Builder.

В DarkCore Ramet

Каркас готов, дальше нужен массовый залив

DarkCore Ramet запускает кампании в Unity Ads пачками: выберите приложения и гео, посмотрите превью, очередь отправит их в темпе, который позволяет Unity.

Все статьи