GameDev Guide
Глава 814 мин чтенияПроверено: 5 сентября 2026 г.

Инициализация SDK и жизненный цикл игры

После этой главы вы сможете корректно инициализировать SDK Яндекс Игр, пережить сбой его загрузки без зависания игры, вызвать LoadingAPI.ready() в момент, который принимает модерация, и разметить геймплей через GameplayAPI.

Содержание главы

Скрипт /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 из чужих примеров не нужно.

Правильный порядок жизненного цикла

Каноничная последовательность выглядит так:

  1. Браузер загружает /sdk.js.
  2. await YaGames.init() — получаем ysdk.
  3. Подписываемся на game_api_pause / game_api_resume — до того, как в игре что-нибудь заиграет.
  4. Загружаем ресурсы игры (это можно делать параллельно с шагом 2).
  5. Показываем меню или игровой экран, с которым уже можно взаимодействовать.
  6. ysdk.features.LoadingAPI?.ready().
  7. Дальше по ходу игры — 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Ожидает инициализации
ITIs loader: trueЛоадер SDK актуальный — так и должно быть
IFIs loader: falseСтарый лоадер, нарушение пункта 1.19.1
Мигает фиолетовымWaiting for call "ready"SDK инициализирован, ready() ещё не вызван
ЗелёныйThe game called ready after … msready() вызван, время указано в миллисекундах
Красный"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_resumeGameplayAPI.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, гостевая игра без авторизации и лимиты на запросы, которые легко превысить, если сохраняться на каждый кадр.

Источники

Официальная документация платформы — единственный источник истины по её требованиям.

Нужно подробнее?

Полное обучение разбирает те же темы глубже: уроки с примерами, готовые структуры проектов, развёрнутые чек-листы и файл инструкций для Claude Code — он входит в стоимость. Бесплатные главы при этом остаются открытыми и продолжают дополняться.

Что входит в полное обучение