У рекламы в Яндекс Играх неприятная асимметрия: кода — пятнадцать строк, требований — целый раздел. Пункт 4.4 отвечает за момент показа, 4.7 — за паузу звука и геймплея, 1.16 запрещает самодельные блоки, похожие на рекламные. Плюс одна деталь, которую пропускает почти каждое руководство: платформа показывает полноэкранную рекламу на старте вашей игры сама, без единой строчки вашего кода, — и обработать её всё равно придётся вам. Разберём все форматы, напишем модуль и пройдёмся по требованиям, на которых чаще всего спотыкаются.
Четыре рекламных сущности
| Что это | Как вызывается | Кто решает, когда показать |
|---|---|---|
| Полноэкранный блок (interstitial) | ysdk.adv.showFullscreenAdv() | Игра вызывает, но частоту ограничивает платформа |
| Rewarded video | ysdk.adv.showRewardedVideo() | Игрок сам нажимает кнопку; частота не ограничена |
| Sticky-баннер | showBannerAdv() / hideBannerAdv() | По умолчанию платформа; API включается в Консоли |
| Стартовая полноэкранная реклама | Никак — её нет в вашем коде | Платформа, автоматически, на старте всех игр |
Полноэкранный блок и блок с вознаграждением создаются автоматически при подключении монетизации в Консоли — руками их заводить не нужно. Ещё один факт из документации, полезный при отладке: реклама показывается только в онлайн-режиме. Играете без интернета — блоков не будет, и это не баг в вашем коде.
Полноэкранная реклама
Документированная форма вызова — объект с полем callbacks:
ysdk.adv.showFullscreenAdv({
callbacks: {
onOpen: () => console.log('Реклама открыта.'),
onClose: (wasShown) => console.log(wasShown ? 'Показана и закрыта.' : 'Не показана.'),
onError: (error) => console.error('Ошибка вызова.', error),
},
});Метод ничего не возвращает — весь результат приходит в колбэки. Ключевой из них onClose: по документации он вызывается при закрытии рекламы, после ошибки, а также если реклама не открылась из-за слишком частого вызова. То есть onClose — это единственная точка, которая срабатывает гарантированно, а wasShown === false — единственный доступный вам сигнал «показа не было».
Важно
Игра должна продолжаться одинаково при wasShown === true и wasShown === false. Если следующий уровень запускается только внутри «успешной» ветки, у части игроков игра просто зависнет на чёрном экране — это отказ по пункту 1.14 (игра не запускается / зависает).
Чтобы не размазывать логику по колбэкам, оберните вызов в промис. Обратите внимание на защиту от двойного разрешения: после onError документация обещает ещё и onClose.
// ads.js
export function showInterstitial(ysdk) {
return new Promise((resolve) => {
let settled = false;
const finish = (wasShown) => {
if (settled) return;
settled = true;
resolve(wasShown);
};
ysdk.adv.showFullscreenAdv({
callbacks: {
onClose: (wasShown) => finish(Boolean(wasShown)),
onError: (error) => {
console.error('[adv] fullscreen error', error);
finish(false);
},
},
});
});
}Использование — всегда после действия игрока, никогда не в середине геймплея:
async function onNextLevelClick() {
await showInterstitial(ysdk); // Результат нужен только для аналитики.
startLevel(currentLevel + 1); // Запускаем уровень в любом случае.
}Кто управляет частотой показов
Здесь придётся разойтись с большей частью русскоязычных туториалов. В них обычно фигурирует «минимальный интервал 60 секунд», который предлагается отслеживать самому — через Date.now() и собственный таймер.
В актуальной документации никакого числового интервала нет. Проверено в сентябре 2026 года: на странице о рекламе сказано ровно две вещи — частота вызова rewarded video не ограничена, а частота вызова полноэкранного блока управляется платформой Яндекс Игры. В требованиях к расположению рекламы добавлено, что при слишком частом вызове в консоли браузера появится предупреждение.
Практический вывод: не стройте собственный тротлинг как источник истины. Правильная схема — вызывать полноэкранный блок в тех точках, где это уместно по пункту 4.4, и корректно обрабатывать wasShown === false. Платформа сама не покажет лишнего. Если вы всё же добавляете свой интервал (например, чтобы не дёргать SDK на каждом коротком уровне), относитесь к нему как к продуктовому решению, а не как к требованию платформы.
Частая ошибка
Показ рекламы по таймеру, оторванному от действий игрока, приведён в документации как «Пример неправильного вызова»:
setInterval(() => ysdk.adv.showFullscreenAdv(), 180000)Рекламная сеть Яндекса считает случайные клики по блокам признаком рекламного фрода. Реклама, вылезающая посреди активного взаимодействия, гарантированно их порождает.
Rewarded video
Отличие одно, но принципиальное: награду выдаёт onRewarded, а не onClose.
export function showRewarded(ysdk) {
return new Promise((resolve) => {
let rewarded = false;
let settled = false;
const finish = () => {
if (settled) return;
settled = true;
resolve(rewarded);
};
ysdk.adv.showRewardedVideo({
callbacks: {
onRewarded: () => { rewarded = true; },
onClose: () => finish(),
onError: (error) => {
console.error('[adv] rewarded error', error);
finish();
},
},
});
});
}async function onExtraLifeClick() {
const rewarded = await showRewarded(ysdk);
if (rewarded) {
grantExtraLife();
} else {
showToast('Реклама недоступна, попробуйте позже');
}
}onRewarded вызывается, когда просмотр засчитан, — именно в этот момент награда честно заработана. Если выдавать её в onClose, награду получит и тот, кто закрыл ролик на первой секунде.
Требования к кнопке тоже конкретные:
- 4.5 — просмотр только по желанию игрока, например по нажатию специальной кнопки;
- 4.5.1 — из текста или кнопки однозначно понятно, что сейчас будет реклама за вознаграждение и что именно игрок получит. «Получить 100 монет за просмотр рекламы» — да; иконка подарка без подписи — нет;
- 4.5.2 — награда должна быть дополнительным бонусом (бустеры, дополнительные попытки, пропуск уровня) и не должна определять саму возможность продолжить игру.
Sticky-баннер
По умолчанию баннер появляется при запуске игры и висит всю сессию — от вас ничего не требуется, только настройка расположения в Консоли на вкладке Реклама (для мобильных — позиция в портретной и альбомной ориентации, для десктопа — включение показа справа).
Управлять им из кода можно, только если в той же вкладке включена опция Использовать API для показа sticky-баннера. Без неё методы вызовутся, но эффекта не будет.
const { stickyAdvIsShowing, reason } = await ysdk.adv.getBannerAdvStatus();
if (!stickyAdvIsShowing && !reason) {
await ysdk.adv.showBannerAdv();
}
// Перед катсценой или на полноэкранном экране результатов:
await ysdk.adv.hideBannerAdv();Все три метода возвращают промис с полем stickyAdvIsShowing, а getBannerAdvStatus() и showBannerAdv() — ещё и опциональное reason:
reason | Что означает |
|---|---|
ADV_IS_NOT_CONNECTED | Баннеры не подключены |
UNKNOWN | Ошибка показа рекламы на стороне Яндекса |
Полезное следствие: reason === 'ADV_IS_NOT_CONNECTED' в черновике — это обычно не ошибка кода, а незавершённая настройка монетизации.
Стартовая реклама платформы — то, что ломает игры
Платформа автоматически показывает полноэкранную рекламу на старте всех игр. У неё нет колбэков: вы не узнаете о ней ни из showFullscreenAdv, ни из какого-либо промиса. Единственный способ её отследить — события game_api_pause и game_api_resume.
Что это значит на практике: если ваша игра запускает музыку и игровой цикл сразу после загрузки, игрок услышит вашу музыку поверх рекламного ролика и вернётся в игру, где уже потерял тридцать секунд. Это нарушение пункта 4.7 и одна из причин отказа в собственном списке Яндекса.
Эти же события приходят при показе interstitial и rewarded, при открытии окна покупок, при переключении вкладок браузера и при сворачивании окна. То есть одна подписка закрывает сразу несколько требований — 1.3 (звук останавливается при потере фокуса) и 4.7.
// pause.js
let gameStarted = false;
let isPaused = false;
function handlePause() {
isPaused = true;
audioCtx.suspend(); // Web Audio API.
cancelAnimationFrame(rafId); // Останавливаем игровой цикл.
}
function handleResume() {
isPaused = false;
audioCtx.resume();
if (!gameStarted) {
startGame(); // Стартовая реклама закончилась — только теперь запускаемся.
} else {
rafId = requestAnimationFrame(tick);
}
}
export function initPause(ysdk) {
ysdk.on('game_api_pause', handlePause);
ysdk.on('game_api_resume', handleResume);
// Если пауза уже пришла (идёт стартовая реклама) — ждём resume.
if (!isPaused) startGame();
}
function startGame() {
gameStarted = true;
rafId = requestAnimationFrame(tick);
}Отписаться при необходимости можно теми же именами: ysdk.off('game_api_pause', handlePause).
Совет
События согласованы с разметкой геймплея: при game_api_pause платформа сама вызывает GameplayAPI.stop(), при game_api_resume — GameplayAPI.start(). Дублировать эти вызовы вокруг рекламы не нужно. Причём если игра уже была остановлена вашим GameplayAPI.stop() (например, игрок открыл меню), то после game_api_resume метод start() не вызовется — состояние вашей разметки не сломается.
Собственную рекламу на старте (то есть свой вызов showFullscreenAdv сразу после загрузки) показывать можно, но только после срабатывания Game Ready — это прямая сноска в требованиях к расположению рекламы.
Когда показывать: пункт 4.4 в одной таблице
Вне игрового процесса — после запуска игры и после любого неигрового действия: выбор уровня, кнопки «Новый уровень», «Магазин», «Коллекция». Внутри игрового процесса всё зависит от механики и длины уровня:
| Тип игры | Уровни короче 5 минут | Уровни длиннее 5 минут |
|---|---|---|
| Условно-пошаговые (тесты, викторины, пазлы, merge) | Сразу после действия игрока, без предупреждения | Так же |
| В реальном времени | Внутри уровня нельзя — только после неигрового действия | Можно в любой момент, но с паузой и предупреждением |
Предупреждение — это заметная плашка с обратным отсчётом («Реклама через: 2, 1») длительностью 2 секунды, одна на один показ. Во время предупреждения и самой рекламы игра должна быть на паузе; анимацию «замораживать» не обязательно, главное — недоступность игровых действий.
Про задержку: максимальная допустимая задержка между действием игрока и началом рекламы — 0,33 секунды (330 мс). При этом модерация считает нарушением только явную задержку больше 2 секунд, а пограничные случаи проходят проверку. Ориентируйтесь на 0,33 с как на цель — то есть вызывайте рекламу в обработчике клика, а не после анимации перехода на 1,5 секунды.
Чего делать нельзя
| Пункт | Запрет |
|---|---|
| 4.1 | Реклама только через SDK Яндекс Игр; никаких сторонних рекламодателей, статичных баннеров и текстовых вставок |
| 4.6.2 | Кастомные RTB-баннеры запрещены; дополнительными блоками считаются только sticky-баннеры |
| 1.16 | Нельзя модифицировать блоки и имитировать их — свой «полноэкранный блок» или RV со своими кнопками |
| 4.7 | Звук и геймплей обязаны ставиться на паузу на время interstitial и rewarded |
| 4.3 | Ориентация рекламных блоков соответствует ориентации игры |
| 4.2 | Внутренний прогресс сохраняется после перехода по рекламе и возврата обратно |
Отдельно: если в игре продаётся отключение рекламы, после покупки её действительно не должно быть — включая sticky-баннер.
Осторожно
«Звук в игре не ставится на паузу во время показа рекламы» — пункт из официальной таблицы частых причин отказа. Проверяется он за десять секунд: модератор открывает игру в наушниках. Один общий модуль паузы, подписанный на game_api_pause, снимает этот риск целиком.
Как проверить перед отправкой
Реклама не работает в локальном запуске «просто из папки» — нужна реальная платформа. Документация предлагает два способа: prod-окружение (локальный запуск с проксированием на боевой SDK) и режим черновика в Консоли. В обоих случаях показывается настоящая реклама.
Минимальный прогон:
- Открыть игру и ничего не трогать: дождаться стартовой рекламы платформы. Музыки поверх ролика быть не должно, после закрытия игра начинается с начала.
- Вызвать interstitial дважды подряд — второй раз, скорее всего, придёт
wasShown === false. Игра должна продолжиться так же, как приtrue. - Нажать кнопку rewarded и закрыть ролик досрочно — награды быть не должно.
- Посмотреть ролик до конца — награда выдана ровно один раз.
- Переключить вкладку браузера во время игры — звук замолкает, цикл останавливается.
- Перейти по рекламному объявлению и вернуться — прогресс на месте (пункт 4.2).
- Проверить консоль: предупреждений о слишком частом вызове быть не должно.
Что дальше
Реклама — половина монетизации, и та половина, где вы не управляете почти ничем: ни ценой показа, ни частотой. Вторая половина — внутриигровые покупки: там вы задаёте каталог, цену и логику выдачи товара, но и требований к корректности больше — от обязательного консумирования до запрета хардкодить название и иконку валюты. Ими и займёмся в следующей главе.