Скрипт /sdk.js подключён, но сам по себе он ничего не делает. Пока не выполнен YaGames.init(), у игры нет объекта ysdk — а значит, нет ни рекламы, ни сохранений, ни лидербордов. И, что важнее для публикации, платформа не знает, что игра вообще загрузилась.
Эта глава — про порядок вызовов. Модерация проверяет не наличие методов в коде, а момент, в который они срабатывают. Именно поэтому Game Ready занимает сразу две строки в официальном списке частых причин отказа: «Game Ready не используется совсем» и «Game Ready работает некорректно» (данные проверены в сентябре 2026 года).
YaGames.init(): что он возвращает
YaGames.init() возвращает промис, который резолвится объектом SDK. Все дальнейшие вызовы — ysdk.adv, ysdk.getPlayer(), ysdk.leaderboards, ysdk.features — идут через этот объект.
const ysdk = await YaGames.init();Или без await, если вам удобнее промисы:
YaGames.init()
.then((ysdk) => {
ysdk.features.LoadingAPI?.ready();
})
.catch(console.error);Единственное жёсткое ограничение: скрипт /sdk.js должен быть подключён до выполнения YaGames.init(). Если вы видите в консоли Uncaught ReferenceError: YaGames is not defined — нарушен порядок подключения. Если Uncaught ReferenceError: ysdk is not defined — вы вызвали методы SDK раньше, чем завершилась инициализация.
Параметр signed
init() принимает один документированный параметр — signed: boolean. Он относится к защите покупок от накруток и определяет, в каком виде методы платежей возвращают данные.
| Значение | Когда выбирать | Что вернут методы покупок |
|---|---|---|
signed: false (по умолчанию) | Покупки обрабатываются на клиенте | Данные в открытом виде: productID, purchaseToken, developerPayload |
signed: true | Покупки проверяются на вашем сервере | Только зашифрованные данные в поле signature |
// Серверная проверка покупок
const ysdk = await YaGames.init({ signed: true });Подпись — это две строки base64 вида <подпись>.<данные>, которые вы проверяете у себя по HMAC-SHA256 секретным ключом игры (ключ появляется в Консоли после подключения внутриигровых покупок). Тот же signed можно передать не в init(), а точечно — в ysdk.getPayments({ signed: true }). Если сервера у вас нет, оставьте значение по умолчанию: signed: true без проверки на бэкенде не даст ничего, кроме нечитаемых ответов.
Осторожно
В старом коде и во многих руководствах встречается опция scopes — в init() или в getPlayer(). В текущей документации Яндекс Игр слова «scopes» нет ни на одной странице (проверено в сентябре 2026 года). У init() документирован только signed, у getPlayer() — тоже только signed. Копировать scopes из чужих примеров не нужно.
Правильный порядок жизненного цикла
Каноничная последовательность выглядит так:
- Браузер загружает
/sdk.js. await YaGames.init()— получаемysdk.- Подписываемся на
game_api_pause/game_api_resume— до того, как в игре что-нибудь заиграет. - Загружаем ресурсы игры (это можно делать параллельно с шагом 2).
- Показываем меню или игровой экран, с которым уже можно взаимодействовать.
ysdk.features.LoadingAPI?.ready().- Дальше по ходу игры —
GameplayAPI.start()иGameplayAPI.stop().
Шаг 3 стоит раньше шага 4 не случайно. Платформа автоматически показывает полноэкранную рекламу на старте всех игр, и у этой рекламы нет собственных колбэков — единственный способ узнать о ней — события паузы. Если ваша игра включает музыку сразу после загрузки, а подписка появилась позже, звук успеет заиграть поверх рекламного ролика. Это нарушение пункта 4.7.
Пока SDK грузится
init() — сетевой вызов, и его длительность вы не контролируете. Не выстраивайте загрузку в цепочку «сначала SDK, потом ресурсы»: запускайте оба процесса параллельно.
async function boot() {
// init и загрузка ассетов идут одновременно
const [sdk] = await Promise.all([
initSdk(), // см. ниже
loadAssets(), // ваша загрузка спрайтов, звуков, уровней
]);
ysdk = sdk;
if (ysdk) {
ysdk.on('game_api_pause', onPlatformPause);
ysdk.on('game_api_resume', onPlatformResume);
}
showMainMenu(); // меню отрисовано и кликабельно
hideLoadingScreen(); // свой лоадер убран
// Только теперь сообщаем платформе о готовности
requestAnimationFrame(() => {
ysdk?.features?.LoadingAPI?.ready();
});
}
boot();Пока идёт загрузка, пользователь видит сначала загрузочный экран самой платформы (пульсирующая иконка игры и надпись «Загрузка»), а потом ваш. Учтите: экран платформы можно скрыть, просто нажав на него, — модерация этим пользуется. И ваш собственный загрузочный экран тоже проверяют: сторонние ссылки, чужие логотипы и материалы, нарушающие авторские права, на нём запрещены.
Совет
requestAnimationFrame вокруг ready() — не требование документации, а практическая страховка: он гарантирует, что кадр с меню уже отрисован, а не только помещён в DOM. Разница в один кадр, но она отделяет «игра доступна» от «игра вот-вот станет доступна».
Обработка ошибок инициализации
Список кодов ошибок init() официально не публикуется, поэтому обрабатывать отказ нужно обобщённо: как «SDK недоступен». Ключевое правило — игра обязана запуститься даже в этом случае. Зависший на загрузке экран попадает под пункт 1.14 («Игра не запускается»), и это отдельная причина отказа.
let ysdk = null;
async function initSdk() {
if (typeof YaGames === 'undefined') {
// Скрипт не загрузился или игра открыта вне платформы
console.warn('SDK недоступен, играем в автономном режиме');
return null;
}
try {
return await YaGames.init();
} catch (err) {
console.error('Ошибка инициализации SDK:', err);
return null;
}
}Дальше по коду везде используется опциональная цепочка ysdk?., а сохранения падают в localStorage. В официальных примерах опциональный вызов стоит и на самих фичах — ysdk.features.LoadingAPI?.ready() — потому что члены features не гарантированы. В примерах выше добавлена ещё одна проверка (ysdk?.), чтобы код пережил и полное отсутствие SDK.
Побочный бонус этого подхода: игра остаётся запускаемой локально в обычном браузере, без прокси. Для полноценной локальной отладки с работающим SDK используйте npx @yandex-games/sdk-dev-proxy — скачивать sdk.js к себе в проект нельзя.
LoadingAPI.ready(): требование 1.19.2
Метод нужно вызывать, когда игра загрузила все ресурсы и готова к взаимодействию с пользователем. Формулировка документации: в момент вызова все элементы должны быть готовы к взаимодействию с игроком и не должно оставаться экранов загрузки.
Модерация считает момент правильным, если индикатор Game Ready на debug-панели становится зелёным тогда, когда доступно меню или когда игра доступна для взаимодействия (включая стартовую анимацию).
| Момент вызова | Вердикт |
|---|---|
| Появилось меню, кнопки нажимаются | Корректно |
| Игра доступна для взаимодействия, идёт вступительная анимация | Корректно |
| Ещё видны прогресс-бары, тробберы, чёрный экран | Некорректно |
| Через несколько секунд после того, как игра стала готова | Некорректно |
| Не вызван вообще в течение 90 секунд | Считается, что Game Ready не встроен |
Отсюда вытекает главная ловушка. Нельзя привязывать ready() ко времени — к setTimeout, к длительности анимации логотипа, к «примерно трём секундам». Документация прямо предупреждает: если вызов привязан к конкретному моменту времени, а не к реальной готовности игры, это станет заметно при ручном скрытии загрузочного экрана платформы, и модерация отметит это как нарушение. Проверяют оба сценария — и когда экран платформы скрыт нажатием, и когда он исчез сам.
Важно
Ровно два пункта из официального списка частых причин отказа посвящены этому методу: Game Ready не интегрирован вообще и Game Ready вызывается в неверный момент жизненного цикла. Оба относятся к пункту 1.19.2. Проверить своё поведение можно за минуту на debug-панели — это дешевле, чем ждать снятия кулдауна после отказа.
Проверка на debug-панели
Запустите игру с параметром debug-mode=16 в адресной строке (https://yandex.ru/games/app/XXXX?debug-mode=16) или кнопкой «Открыть с debug-панелью» в Консоли разработчика.
| Индикатор | Всплывающий текст | Значение |
|---|---|---|
W | — | Ожидает инициализации |
IT | Is loader: true | Лоадер SDK актуальный — так и должно быть |
IF | Is loader: false | Старый лоадер, нарушение пункта 1.19.1 |
| Мигает фиолетовым | Waiting for call "ready" | SDK инициализирован, ready() ещё не вызван |
| Зелёный | The game called ready after … ms | ready() вызван, время указано в миллисекундах |
| Красный | "ready" called on timeout | Прошло 90 секунд — Game Ready считается не встроенным |
Частая ошибка
Если на панели горит IF, значит SDK подключён по устаревшему адресу вида https://yandex.ru/games/sdk/v2. Он до сих пор отвечает, поэтому игра будет работать — и всё равно получит отказ по пункту 1.19.1. Актуальный вариант: <script src="/sdk.js"></script> для игр, размещённых на серверах Яндекса, и https://sdk.games.s3.yandex.net/sdk.js для интеграции через свой домен.
GameplayAPI.start() и stop()
Разметка геймплея говорит платформе, когда игрок реально играет, а когда сидит в меню или смотрит рекламу. Если вы её используете, моменты вызова должны строго соответствовать документации — это пункт 1.19.3.
GameplayAPI.start() | GameplayAPI.stop() |
|---|---|
| Запуск уровня | Прохождение уровня или проигрыш |
| Закрытие меню | Вызов меню |
| Снятие с паузы | Пауза в игре |
| Возобновление после рекламы | Показ полноэкранной или rewarded-рекламы |
| Возвращение в текущую вкладку браузера | Уход в другую вкладку |
Удобно завернуть вызовы в пару функций с флагом состояния, чтобы не дублировать события:
let gameplayActive = false;
function gameplayStart() {
if (gameplayActive) return;
gameplayActive = true;
ysdk?.features?.GameplayAPI?.start();
}
function gameplayStop() {
if (!gameplayActive) return;
gameplayActive = false;
ysdk?.features?.GameplayAPI?.stop();
}
function openMenu() {
gameplayStop();
// ...показать меню
}
function startLevel(level) {
// ...построить уровень
gameplayStart();
}Важный нюанс, который экономит время: события паузы уже согласованы с разметкой геймплея. При срабатывании game_api_pause платформа вызывает GameplayAPI.stop(), при game_api_resume — GameplayAPI.start(). Дублировать эти вызовы в своих обработчиках не нужно — в них занимайтесь звуком и игровым циклом:
function onPlatformPause() {
audio.muteAll();
pauseGameLoop();
}
function onPlatformResume() {
audio.unmuteAll();
if (!menuIsOpen) resumeGameLoop();
}Логика продумана и на случай наложения состояний: если игра уже была остановлена вашим GameplayAPI.stop() (игрок открыл меню), а затем сработал game_api_pause, то при последующем game_api_resume метод start() вызван не будет. Игрок вернётся во вкладку и увидит меню, а не внезапно продолжившийся уровень.
Проверяется всё это на той же debug-панели: иконка геймпада 🎮 должна становиться зелёной при старте геймплея и красной при остановке. Сценарии, которые смотрит модерация: запуск и завершение уровня, открытие и закрытие игрового меню, открытие и закрытие меню покупок, запуск и закрытие рекламы, потеря и возврат фокуса вкладки.
Что дальше
Инициализация даёт вам объект ysdk — дальше начинается собственно интеграция. Следующий шаг: объект Player, сохранение прогресса через setData/getData и setStats/incrementStats, гостевая игра без авторизации и лимиты на запросы, которые легко превысить, если сохраняться на каждый кадр.