Оптимизация текстурных атласов: где проседает кадр
Сцена мобильной игры растёт итерациями: сначала десяток объектов на уровне, потом сотня, и бюджет времени кадра незаметно перестаёт сходиться. Просадки кадров на слабых устройствах и долгая загрузка уровня почти никогда не связаны со сценариями или логикой — в проектах, которые мы разбирали, причина чаще в том, как собран текстурный атлас мобильной игры.
Типовая ошибка — один большой лист на всю категорию ресурсов: интерфейс, окружение и эффекты лежат вместе, и движок поднимает в память весь лист ради двух-трёх нужных элементов. Параллельно растёт объём сборки: часть листа не сжимается, часть каналов остаётся без потерь, а декодирование крупного изображения занимает заметную долю времени старта уровня.
Отдельно бьёт неаккуратный формат: элементы без отступа по краям, разные каналы прозрачности в одном листе, смешивание ресурсов с разной требуемой детализацией. На слабых устройствах это даёт рваные края и повторную выгрузку-загрузку листа там, где хватило бы подгрузки одного фрагмента. Оптимизация текстурных атласов начинается не с упаковки, а с профиля: что и когда реально попадает на экран.
Мы снимаем этот профиль на целевых устройствах, затем режем листы по сценам и группам видимости, подбираем формат сжатия под содержимое листа и добавляем мип-уровни там, где объект уходит в глубину кадра. Сборку собираем в том же порядке, что и у вас, — упаковщик вроде TexturePacker подключается как шаг автоматизации, чтобы ручная правка листов не возвращалась в проект. Разбираем, как это закрывается.
Что даёт перепаковка атласов мобильной сцены
Правильно собранный атлас в первую очередь сокращает объём ресурсов сборки: повторяющиеся элементы и мелкие изображения укладываются в общие листы, дубликаты и лишние поля по краям уходят. Из поставки пропадает заметная доля исходных данных, а вместе с ней — время на их распаковку и проверку целостности при первом запуске.
Время загрузки сцены сокращается за счёт двух эффектов: меньше файлов нужно открыть и прочитать, меньше данных декодируется при переходах между экранами. На среднем устройстве разница заметна пользователю — экран готовности не держит его лишние секунды, а фоновая подгрузка следующих сцен не конкурирует с отрисовкой текущей.
Расход памяти на устройстве падает предсказуемо: один крупный лист вместо десятков мелких снижает накладные расходы на хранение и переключение, а единый формат хранения убирает постоянные освобождения и повторные загрузки. Это тот запас, который остаётся под графику на бюджетных моделях и определяет, доживёт ли сцена до конца без принудительной выгрузки.
Стабильность кадров растёт напрямую: чем меньше переключений материалов на кадр, тем ровнее время отрисовки и реже просадки на пиках. Отказы по нехватке памяти, которые обычно всплывают на длинных сессиях, уходят вместе с дроблением ресурсов на множество мелких файлов.
Итог проверяется по четырём измеримым признакам: вес сборки стал меньше, сцена грузится быстрее, память расходуется экономнее, кадры идут ровно без провалов. Если хотя бы один пункт не сдвинулся, перепаковку атласов стоит пересобрать заново — по замерам, а не по ощущениям.
Режимы упаковки и сжатия: сравнение подходов
Ресурсы мобильной игры упираются в два ограничения: объём видеопамяти и число переключений материалов на кадр. Режим упаковки и сжатия выбирают не «по вкусу», а под содержимое атласа — градиенты, мелкие иконки интерфейса и полноэкранные фоны ведут себя по-разному.
Сжатие без потерь сохраняет каждый пиксель, но в памяти такая текстура всё равно разворачивается целиком — выигрыш остаётся только в размере сборки. Блочное сжатие с потерями (ASTC) держит данные сжатыми прямо в памяти ускорителя: объём и пропускная способность падают, но появляются артефакты на тонких линиях и градиентах.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Без потерь | Интерфейс, шрифты, маски, превью | В памяти разворачивается целиком, видеопамять не экономит |
| Блочное с потерями | Крупные фоны, карты материалов | Артефакты на градиентах, требования к кратности блока |
| Разбиение на страницы | Атлас не влезает в лимит устройства | Больше переключений материалов на кадр |
| Плотная упаковка с ротацией | Разнородные спрайты, чёткая сетка | Ротация ломает девятисегментные элементы и шейдеры |
Плотность упаковки конфликтует с разбиением на страницы. Чем плотнее атлас, тем меньше страниц и переключений, но ротация спрайтов ломает девятисегментные элементы и шейдеры, зависящие от координат. Разбиение, наоборот, упрощает поддержку артов, но добавляет переключений на кадр.
Поэтому мы не фиксируем один режим: интерфейс и шрифты идут без потерь, крупные фоны — блочным сжатием, переполненные атласы делятся по частоте совместного использования. Профиль закрепляется в сборочном скрипте, и новый арт не ломает раскладку.
Порядок работ: от замера до передачи проекта
Атлас перепаковываем не «на глаз», а по результатам замера: сначала снимаем профиль на целевых устройствах и находим, что именно упирается — заполнение текстурного блока, объём занимаемой видеопамяти, число вызовов отрисовки или время загрузки сцены. Дальше идёт последовательность шагов, где каждое решение опирается на данные предыдущего, а не на привычку разработчика.
- Замер узких мест. Профилируем проблемные сцены и меню на репрезентативной выборке устройств — от слабых до топовых. Фиксируем базовые метрики: время кадра, потребление памяти, длительность загрузки, число переключений материала.
- Схема разбиения атласа. Группируем спрайты по частоте совместного появления в кадре и по общему материалу, считаем бюджет площади и запас на технологический отступ между элементами, чтобы не ловить «протекание» краёв на мип-уровнях.
- Перепаковка и пересборка. Собираем атласы по утверждённой схеме, приводим размеры к удобным для сжатия значениям, обновляем привязки в ресурсах и проверяем, что сборка проходит без ручных правок.
- Проверка на целевых устройствах. Прогоняем те же сцены повторно и сверяем метрики с базовыми: смотрим, ушло ли узкое место и не появилось ли новое — например, рост времени загрузки из-за дробления файлов.
- Стабилизация и регресс. Фиксируем эталонные замеры и добавляем автопроверку схемы упаковки, чтобы следующая порция графики не сломала достигнутый результат.
- Передача проекта команде. Отдаём схему разбиения, скрипты перепаковки, регламент добавления новых ресурсов и короткую инструкцию для художников — дальше команда заказчика ведёт упаковку сама, без нас.
Каждый этап заканчивается артефактом: замер, схема, обновлённые атласы, отчёт по устройствам, регламент. Так результат воспроизводим, а не держится на одном инженере.
Артефакты: схемы разбиения, профили памяти, регламенты
После работ у команды заказчика остаётся не отчёт, а рабочий комплект: по нему новый инженер воспроизводит сборку атласов и получает тот же результат без обращения к нам. Каждый документ привязан к конкретному шагу конвейера ресурсов, поэтому расхождение между «как договорились» и «как собирается» видно сразу — на этапе проверки, а не на устройстве игрока.
- Схема разбиения ресурсов — какие группы изображений сводятся в один атлас, по какому признаку (частота одновременного показа, общий материал, экран), какие остаются отдельными файлами. С указанием предельного размера листа и правил добора свободного места.
- Профиль памяти приложения — замеры потребления на целевых устройствах до и после правок, разбитые по сценам: загрузка, бой, интерфейс, длительная сессия. Служит эталоном для сравнения при следующих релизах.
- Правила именования ресурсов — единый шаблон для изображений, атласов и производных файлов, чтобы сборщик автоматически находил зависимости, а поиск нужного листа не превращался в перебор директорий.
- Регламент ревизий графики — кто и когда пересматривает состав атласов, что считается поводом для пересборки (новый набор контента, рост потребления, жалобы на подгрузки), как фиксируется результат.
- Документация по сборке ресурсов — порядок шагов, состав инструментов и точки контроля с проверками: незаполненные области, дубли, ресурсы вне схемы. Новый человек проходит путь от исходников до готового набора без устных пояснений.
- Карта владения — какие файлы и настройки закреплены за конкретными ролями, что меняется только через ревизию, а что можно править локально.
Комплект передаём в виде, пригодном для правок: схемы и правила лежат рядом с проектом и версионируются вместе с ним, а не в переписке. Через несколько релизов команда возвращается к документам, а не к нам — это и есть критерий, что работа сделана.
Кейс: убрали просадки на слабых устройствах
Что было до работ
К нам пришёл проект, где графика росла быстрее, чем её успевали приводить в порядок: каждый новый экран тянул собственный набор текстур, часть изображений дублировалась сразу в нескольких атласах, а редкие ассеты лежали в общем пуле и грузились всегда. На нижней границе поддерживаемых устройств это давало просадки в бою и на переходах между сценами — память упиралась в потолок, а подгрузка шла ровно в момент действия игрока.
Что изменили
Пересобрали атласы по фактической совместной встречаемости: ресурсы одного экрана — в один набор, редкие вынесли отдельно с догрузкой по требованию. Дубликаты заменили ссылками на общий источник, число кадров в анимациях ограничили, форматы сжатия подобрали под то, что устройство декодирует аппаратно.
- убрали пересечения атласов между экранами;
- переиспользуемые элементы вынесли в общий пул вместо копий;
- редкие сцены перевели на асинхронную догрузку с предзагрузкой в паузах;
- кэш прогрели на старте, чтобы первый вход в бой не собирал ресурсы.
Как измерили результат
Считали по факту, а не на глаз: проект на Unity отдаёт покадровый профиль, и мы опирались на него. Проверяли не на топовых моделях, а на нижней границе списка поддержки — тех устройствах, что реально приносят установки. Один и тот же набор сцен до и после: объём сборки уменьшился примерно на треть, загрузка уровня стала вдвое быстрее, просадки на слабых устройствах ушли, память перестала упираться в потолок.
Что делать, если приложение уже выпущено и обновляется?
Выпущенное приложение обновляется в своём ритме, и останавливать релизы ради переупаковки графики никто не будет. Оптимизация текстурных атласов встраивается в текущий цикл: одна волна — один экран или локация. Каждый шаг при этом обязан быть откатываемым, иначе риск при обновлении перевешивает выигрыш.
Ключевое условие — обратная совместимость ресурсов. Старые сборки на устройствах продолжают читать прежние листы, новые — уже переупакованные: имена слотов и ключи доступа к кадрам сохраняются, меняется только раскладка. Прежний набор остаётся в дистрибутиве до момента, когда доля игроков на устаревших версиях падает до пренебрежимой.
Плавный переход строится на замерах, а не на вере в выигрыш. На каждой волне сравниваем занятую видеопамять, время загрузки уровня и частоту подгрузок во время игры. Если эффект не подтверждается на матрице устройств, волна в релиз не идёт — правки ждут в ветке.
Риски известны и закрываются заранее: артефакты на краях листов при фильтрации и переходе между уровнями детализации, потерянные ссылки после удаления старых кадров, рост размера сборки в переходный период. Возврат к прежней схеме делаем флагом конфигурации, без аварийного обновления.
Так оптимизация идёт параллельно выпуску версий, а не вместо него: каждая волна даёт измеримый срез по памяти и загрузке, а согласованный вид и поведение игры для игрока остаются неизменными.
Фильтрация, прозрачность, память: частые вопросы
Такие вопросы обычно всплывают уже на созвоне, когда черновой атлас собран и видно, где он проседает. Ниже — короткие ответы на то, что чаще всего вызывает сомнения у команды заказчика.
Общий принцип один: решение по атласу принимается по замерам — занимаемая память, время подготовки сцены, число переключений текстур за кадр. Без этих чисел спор о кайме или качестве сжатия остаётся вкусовым.
По краям элементов появляется кайма — это лечится?
Да, это самый частый артефакт, и причина техническая: фильтрация захватывает пиксели соседнего элемента. Закрывается запасом в 2–4 пикселя по краю, куда дублируются крайние пиксели, и корректно собранными уменьшенными копиями листа — иначе кайма проявится на дальних уровнях детализации.
Можно ли держать полупрозрачные элементы в общем атласе?
Можно, но с оговорками: критичны порядок смешивания и способ хранения прозрачности. Если полупрозрачный элемент лежит в одном листе с непрозрачными, он смешивается с чужими пикселями и даёт тёмный ореол — такие элементы выносим в отдельный лист или отдельный слой отрисовки.
Почему после разбиения атласа память выросла?
Потому что каждый лист — отдельная текстура со своим минимальным размером, запасом по краям и своими уменьшенными копиями. Дробим не «по папкам», а по формату сжатия и частоте использования элементов: редкие элементы уезжают в отдельный лист, который грузится по требованию.
Как убедиться, что сжатие не убило качество?
Сравниваем сжатый лист с эталоном без сжатия, смотрим отклонение по каналам, а глазами — на градиентах, тонких линиях и мелком тексте. Элементы, чувствительные к потере деталей, держим на отдельном листе с более щадящим режимом.
С чего начинать, если приложение просто «тормозит»?
С замера. Снимаем объём загружаемых текстур, их число и время подготовки сцены, затем смотрим, что меняется при переходе между экранами. Чаще всего узкое место — не геометрия, а объём текстур и лишние переключения материалов в кадре.
Обсудим задачу: замер сцены, план работ, оценка
Оптимизация текстурных атласов начинается не с правок, а с замера сцены: без цифр любое изменение превращается в угадывание. Чтобы вернуться с оценкой сроков и рабочим планом, а не с общими рекомендациями, нам нужны стартовые данные проекта.
- текущая сборка игры или доступ к репозиторию — снимок реального состояния, а не пересказ;
- перечень сцен, где видны просадки: загрузка уровня, бой, экран магазина, инвентарь;
- статистика по памяти и времени отрисовки, если команда её уже собирает;
- список атласов с размерами и правилами упаковки: какие наборы спрайтов к какому экрану относятся;
- ограничения по целевым устройствам и минимальной поддерживаемой версии системы;
- данные аналитики падений и отзывов — где чаще жалуются на подтормаживания и нагрев корпуса.
По этим данным мы собираем план работ по оптимизации: очередность атласов, что переупаковываем, что дробим по экранам, что выгружаем из памяти между сценами. К плану прикладываем оценку сроков по этапам — так вы видите, на каком шаге появляется измеримый результат.
Порядок начала работ: обсуждаем задачу, согласуем объём первого этапа и точки контроля, после чего фиксируем план и стартуем. Опишите задачу и пришлите то, что уже есть, — вернёмся с планом работ и оценкой.







