Skip to content
E
Egmatic
переходы между сценамиэкран загрузкиасинхронная загрузкапроизводительность игрпул объектов

Лаги при переходах между сценами: как сделать плавную игру

Подёргивание при переходе между сценами почти никогда не связано с отрисовкой. Причина в другом: тот самый кадр, который должен нарисовать новую сцену, заодно вынужден её загружать и строить. Решение — не делать эту работу в кадре перехода: загружать следующую сцену асинхронно и заранее, прятать остатки за экраном загрузки или затемнением и использовать пул объектов, чтобы переключение не вызывало всплеск создания объектов и сборки мусора. В руководстве разобраны три схемы загрузки (блокирующая, асинхронная с экраном загрузки и предзагрузка), API Unity и Godot, на которых они построены, и частые ошибки — тяжёлый код инициализации, несжатые ассеты и отсутствие экрана загрузки, из-за которых рывок видно игроку.

Vladislav Kovnerov22 июля 2026 г.9 мин

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

Решение — не делать эту работу в кадре перехода. Способов три, и почти каждое зависание при переходе объясняется тем, что ни один из них не используется.

Три схемы загрузки

СхемаКак работаетКогда применятьКомпромисс
Блокирующая (синхронная)Загружает и создаёт все объекты в кадре переходаКрошечные сцены, менюЗависает на всё время загрузки; допустимо, только если укладывается в один кадр
Асинхронная + экран загрузкиЗагружает в фоновом потоке, показывает экран загрузки или затемнение, переключает сцену, когда готоваБольшие сцены, загрузка уровнейИгрок видит экран загрузки
Предзагрузка / потоковая подгрузкаНачинает загружать следующую сцену в свободное время текущейОткрытый мир, соседние уровниТратит больше памяти; требует аккуратного планирования

Большинство игр использует все три. Меню загружаются синхронно (они маленькие); при загрузке уровней показывается экран загрузки; открытый мир незаметно подгружает следующий кусок прямо во время игры.

Способ 1 — Загружайте асинхронно, а не в кадре перехода

Это самое эффективное изменение. Перестаньте просить движок загружать следующую сцену в том же вызове, который на неё переключает, и позвольте ему делать это в фоне.

Unity даёт SceneManager.LoadSceneAsync. Он возвращает объект AsyncOperation, у которого progress растёт с 0 до 0.9 по мере загрузки сцены. Установите allowSceneActivation в false, и сцена предзагрузится без переключения; верните true, когда будете готовы её показать. Передайте LoadSceneMode.Additive, чтобы загрузить следующую сцену поверх текущей, а старую выгрузите сами — это стандартный приём для постоянной сцены-«менеджера», поверх которой сменяются игровые сцены. (Реальная ловушка: при allowSceneActivation, равном false, progress останавливается на 0.9 и завершается, только когда вы снова включите активацию, — полоса загрузки, застрявшая на 90%, почти всегда объясняется этим флагом, а не настоящим зависанием.)

Godot даёт ResourceLoader.load_threaded_request, чтобы начать загрузку PackedScene в фоновом потоке. Опрашивайте load_threaded_get_status, пока он не сообщит о завершении, получите результат через load_threaded_get, а затем вызовите change_scene_to_file или создайте экземпляр вручную. В официальном руководстве «Background loading» разобран именно этот приём для экрана анимированной загрузки.

Какой бы движок вы ни использовали, принцип один: загрузка происходит вне кадра перехода, поэтому кадру, который рисует новую сцену, остаётся только нарисовать её.

Способ 2 — Спрячьте остатки работы за экраном загрузки или затемнением

Даже при асинхронной загрузке финальная активация — создание объектов сцены и выполнение их кода первого кадра — всё равно чего-то стоит. Если эту стоимость нельзя уменьшить, спрячьте её. Затемнение, статичная картинка загрузки или анимированный индикатор дают движку один или несколько кадров, когда игрок не смотрит на игровой процесс, и короткое зависание остаётся незамеченным.

Это не обходной приём, а задуманное решение. Экраны загрузки существуют, потому что работа по построению сцены реальна, и игрок предпочитает видеть полосу прогресса, а не замёрзший кадр. Совместите экран с асинхронной загрузкой выше — и всё время перехода игрок видит плавное движение.

Способ 3 — Используйте пул объектов

Создание экземпляров — одна из самых дорогих операций в главном потоке, а удаление ещё хуже: оно отдаёт работу сборщику мусора, и скачок сборки мусора на первом кадре новой сцены — классическая причина рывка сразу после перехода, который сам по себе прошёл гладко.

Пул объектов решает обе проблемы. Держите в памяти готовый набор объектов — пули, врагов, частицы, элементы интерфейса, всё, что появляется постоянно, — и переиспользуйте их вместо того, чтобы каждый раз создавать и удалять. Когда пуля выпущена, вы активируете объект из пула; когда она попала, деактивируете его и возвращаете в пул. Никакого создания объектов при переходе, никакой сборки мусора после.

Пул стоит заводить для того, чего сцена создаёт много. Держать в пуле всё — лишняя трата памяти; не заводить пул вообще — оставляет стоимость создания на кадре.

Способ 4 — Снизьте стоимость первого кадра

Асинхронная загрузка и пул убирают работу из кадра перехода, но сам объём работы тоже можно уменьшить. Обычно виноваты следующие вещи:

  • Тяжёлая инициализация в Awake / OnEnable / _ready / Create. Каждый объект, который выполняет дорогую настройку в своём первом колбэке, делает это во время активации сцены. Перенесите работу, которой не обязательно выполняться в первом кадре, в корутину, отложенный вызов или более поздний кадр. Инициализируйте лениво — только когда объект действительно используется.
  • Несжатые или слишком крупные ассеты. Несжатая текстура 4096×4096 загружается быстро, но занимает много места; сжатая — маленькая, но немного дороже в декодировании. Подбирайте размер и сжатие текстур под то, что сцена реально показывает на экране.
  • Загрузка на GPU в первом кадре. Иногда сама загрузка быстрая, но GPU всё равно должен передать текстуры и скомпилировать шейдеры в первом кадре, где они появляются. Прогревайте шейдеры заранее и подгружайте крупные текстуры постепенно, чтобы всё это не обрушилось разом.
  • Мусор от кода с обилием строк и выделений памяти. Выделение памяти при активации превращается в её очистку в следующий спокойный момент — часто в первую секунду новой сцены.

Профилируйте, прежде чем оптимизировать. Зависание при переходе может объясняться загрузкой, созданием объектов, активацией или работой GPU в первом кадре — и решение для каждого случая своё.

Частые ошибки

ОшибкаСимптомРешение
Загрузка в кадре переходаОдно долгое зависание ровно в момент переключенияЗагружайте асинхронно; предзагружайте или используйте экран загрузки
Создание всех объектов в Awake/_readyРывок при активации объектовВынесите тяжёлую инициализацию из первого колбэка; инициализируйте лениво
Без пула объектовСкачок сборки мусора сразу после перехода, рывки при создании объектовИспользуйте пул для часто создаваемых объектов
Несжатые или слишком крупные ассетыДолгая загрузка, нехватка памятиСжимайте и подбирайте размер текстур и звука
Без экрана загрузкиЗависание полностью видно игрокуЗатемнение или экран загрузки, чтобы скрыть остатки работы
Оптимизация без профилированияВы чините не ту стоимостьПрофилируйте переход; выясните, куда уходит время на самом деле

Роль Egmatic

Проблемы с переходами проще всего ловить, когда их видно в момент появления. С живым предпросмотром вы запускаете смену сцены и наблюдаете тайминг кадров в том же окне — без сборки между обнаружением рывка и проверкой исправления. Поскольку движок кроссплатформенный, переход, который вы настраиваете на десктопе, попадает и на мобильные устройства, и на консоли, где меньший бюджет процессора делает каждую сэкономленную миллисекунду заметнее. О более широком подходе к производительности — сначала профилировать, потом чинить реальную стоимость — читайте в статье Проблемы производительности Blueprint: 7 приёмов оптимизации, а о визуальной стороне переходов между сценами — в материале Дизайн 2D-сцен: как создавать красивые игровые сцены.

Главное

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