Оптимизация текстурных атласов для мобильных игр

Наша компания по разработке видеоигр ведет независимые проекты, совместно с клиентом создает игры и оказывает дополнительные операционные услуги. Опыт нашей команды позволяет нам охватить все игровые платформы и разработать потрясающий продукт, соответствующий видению клиента и предпочтениям игроков.

От иммерсивных приложений до игровых миров и 3D-сцен

Наша выделенная команда для VR/AR/MR-разработки, Unity-продакшна и 3D-моделирования и анимации с собственными кейсами и презентациями.

Посетить персонализированный сайт
Показано 1 из 1Все 242 услуг
Оптимизация текстурных атласов для мобильных игр
Средний
~2 дня
Часто задаваемые вопросы

Наши компетенции

Какие этапы разработки игры?

Последние работы

  • image_games_mortal_motors_495_0.webp
    Разработка игры для компании Mortal Motors
    1422
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Пошаговая стратегия в фэнтези сеттинге With Fire And Sword
    954
  • image_games_second_team_604_0.webp
    Разработка игры для компании Second term
    577
  • image_games_phoenix_ii_606_0.webp
    3D-анимация — тизер для игры phoenix 2.
    638

Мы часто видим проекты, где текстурные атласы настроены по умолчанию. Результат — 40–100 МБ лишней памяти и разрушенный batching. В одном из проектов казуальной игры UI-атлас в RGBA32 занимал 64 МБ только на одной сцене. После нашей оптимизации — 8 МБ. В этой статье мы расскажем, как правильно оптимизировать текстурные атласы для мобильных игр.

Почему атласы — узкое место мобильной оптимизации?

Sprite Atlas в Unity — инструмент понятный, но его неправильная настройка стабильно прибавляет десятки мегабайт к памяти приложения и убивает batching. Типичная ошибка: разработчик включил Sprite Atlas, сложил все UI-спрайты в один атлас, доволен — Draw Calls упали с 80 до 12. Но забыл включить Include in Build в настройках. В итоге атлас генерируется в Edit Mode, но в Release-билде каждый спрайт загружается отдельной текстурой.

Другая история: атлас настроен правильно, batching работает, но размер 2048×2048 RGBA32 — это 16 МБ только на одну текстуру без mipmaps. На устройстве с 2 ГБ RAM суммарный UI-атлас из 4 листов съедает 64 МБ. При переключении языков — все 4 листа в памяти одновременно.

Какой формат сжатия выбрать для атласов?

Это самая недооценённая часть оптимизации. Разработчики часто оставляют RGBA32 или RGBA16 для всех текстур, не задумываясь.

ASTC — стандарт для современных мобильных устройств. Поддерживает блочное сжатие с настраиваемым качеством: ASTC 4×4 даёт высокое качество при 8 bpp, ASTC 8×8 — приемлемое качество при 2 bpp. Для атласов UI используем ASTC 4×4 или 6×6. Для фоновых текстур без мелких деталей — ASTC 8×8. По сравнению с RGBA32, ASTC 6×6 уменьшает размер в 6 раз при почти незаметной потере качества.

ETC2 — fallback для устройств без поддержки ASTC. Поддерживает альфа-канал. Для старых проектов с низким уровнем API всё ещё актуален.

PVRTC — форматы для iOS (PowerVR GPU). Требует текстур квадратной формы со стороной в степень двойки. Если атлас 1024×512 — PVRTC применить нельзя без изменения размера.

Официальная документация Unity по форматам сжатия

Реальный кейс: казуальная игра-пазл, Android+iOS. UI-атласы занимали 128 МБ в памяти (RGBA32, 4 листа 2048×2048). После переключения на ASTC 6×6 для iOS и ETC2 для Android: 128 МБ → 22 МБ. Качество на экране телефона — неотличимо. Время загрузки UI-сцены снизилось с 1,8 с до 0,4 с (в 4,5 раза быстрее).

Сравнение форматов сжатия:

Формат bpp Качество Совместимость
ASTC 4×4 8 отличное iOS A7+, Android с поддержкой ASTC
ASTC 6×6 4 хорошее та же
ETC2 4 хорошее Android (широко)
PVRTC 4bpp 4 среднее iOS (PowerVR)
RGBA32 32 эталон все
Пример расчёта экономии памяти

Если атлас размером 2048×2048 RGBA32 занимает 16 МБ без mipmaps, то при переключении на ASTC 6×6 (4 bpp) размер снижается до 2 МБ. Для четырёх таких атласов экономия составляет 56 МБ.

Стратегия разбиения атласов

Не все спрайты в один атлас — это путь к проблемам. Правильная стратегия:

По сцене/экрану. Спрайты, которые используются только в меню — в атлас menu_atlas. Спрайты геймплея — в gameplay_atlas. Общие элементы (кнопки, рамки, иконки) — в common_atlas. Это позволяет выгружать неиспользуемые атласы при смене сцены.

По частоте использования. Hotpath-спрайты (HP-бар, прицел, таймер) всегда в памяти → core_hud_atlas. Редкие экраны (настройки, магазин) — выгружаются через Addressables при закрытии.

Ограничение размера листа. 2048×2048 — максимум для мобильных. Часть устройств не поддерживает 4096×4096 для сжатых форматов. В Sprite Atlas Settings устанавливаем Max Texture Size = 2048.

Дубликаты. Unity Addressables Analyze → Check Duplicate Bundle Dependencies обнаруживает спрайты, попавшие в несколько атласов. Типичная причина: shared-спрайт (иконка валюты) использован и в меню, и в геймплее без явного указания атласа.

Почему mipmaps вредны для UI?

Для UI-атласов mipmaps отключаем. UI рендерится в Screen Space, объекты не удаляются от камеры — mipmaps бесполезны и увеличивают размер текстуры на 33%. В Texture Import Settings: Generate Mipmaps = false.

Для игровых текстур (3D-объекты, задний план в 2D с масштабированием) — mipmaps обязательны. Без них aliasing и завышенный texture fetch bandwidth.

Пошаговая настройка атласа для мобильной игры

  1. Импортируйте спрайты с настройками: Max Size = 2048, Compression = ASTC 6×6 (или ETC2 для fallback), Generate Mipmaps = false.
  2. Создайте Sprite Atlas (V2): Assets → Create → Sprite Atlas.
  3. В Inspector: Type = Master, Include in Build = true, Allow Rotation = true, Tight Packing = true, Padding = 4.
  4. Добавьте спрайты в Objects for Packing.
  5. Разделите по сценам: создайте отдельные атласы для menu, gameplay, common.
  6. Для управления памятью используйте Addressables: отметьте атласы как Addressable и выгружайте при смене сцены.
  7. Проверьте дубликаты через Addressables Analyze.

Процесс работы над оптимизацией

  1. Аналитика. Собираем профили памяти и draw calls на целевых устройствах. Используем Unity Profiler, RenderDoc, GameAnalytics.
  2. Аудит атласов. Оцениваем текущую стратегию, форматы, размеры, дубликаты.
  3. Проектирование. Разрабатываем новую архитектуру атласов с разбивкой по сценам и частоте использования.
  4. Реализация. Перенастраиваем атласы, сжимаем текстуры, интегрируем Addressables.
  5. Тестирование. Проверяем память, производительность, качество на устройствах с 2 ГБ RAM и ниже.
  6. Деплой. Фиксируем настройки в Version Control, документируем workflow.

Что входит в работу

  • Аудит текущих атласов с отчётом по памяти, draw calls и рекомендациями.
  • Разработка стратегии разбиения под целевую платформу.
  • Настройка сжатия (ASTC, ETC2, PVRTC) с контролем качества.
  • Интеграция Addressables для выгрузки атласов.
  • Документация по поддержке атласов в проекте.
  • Обучение команды работе с новой системой.
  • Пост-релизная поддержка в течение месяца.
Масштаб задачи Ориентировочные сроки
Аудит атласов + отчёт 1–2 дня
Переработка атласной стратегии (1 платформа) 3–7 дней
Полная оптимизация для Android + iOS 2–4 недели
Интеграция с Addressables 2–3 недели

Мы гарантируем качество: наш опыт — более 10 проектов с оптимизацией под мобильные устройства. Оценим ваш проект бесплатно — свяжитесь для аудита. Закажите оптимизацию атласов и получите до 80% экономии памяти без потери качества.

Типичные проблемы производительности игр

Проект запускается на топовых девайсах разработчика без вопросов. На mid-range Android пятилетней давности — 20 fps и перегрев через 5 минут. На iPhone 11 — стабильные 60, но на iPhone XR — просадки в тяжёлых сценах. Мы сталкиваемся с этим каждый день. Оптимизация — не «потом», а архитектурное решение с первого коммита. За 8 лет работы мы провели оптимизацию более чем для 50 игр — от гиперказуалки до AAA на консолях. Гарантируем: после нашего аудита вы получите не просто список проблем, а конкретный план с измеримыми целями и сроками. Закажите аудит производительности — оценим ваш проект за 3 дня и покажем, как снизить draw calls на 40% без потери качества.

Как профилировать производительность игр: инструменты и правила

Прежде чем оптимизировать — измерить. Оптимизация без профилирования — угадывание.

Инструмент Назначение
Unity Profiler CPU/GPU время по системам, GC allocations, audio
Frame Debugger Инспекция каждого draw call в кадре
Memory Profiler Снимок памяти, граф зависимостей ассетов
RenderDoc Глубокий анализ GPU-состояния, актуален для PC/Console
Android GPU Inspector Профилирование GPU на реальном Android-устройстве
Xcode Instruments GPU + memory на iOS (Metal Performance HUD)
Snapdragon Profiler Qualcomm GPU — детальная статистика шейдеров

Профилируйте на целевом железе, а не в редакторе. Editor добавляет оверхед — цифры из Play Mode не репрезентативны. Разделяйте GPU и CPU bottleneck: на CPU много draw calls или тяжёлая логика, на GPU — сложные шейдеры или overdraw. Документация Unity: профилирование на устройстве — обязательный шаг для мобильных игр. Типичное время профилирования одного сценария — 5–8 часов, включая сбор метрик на 3–5 устройствах разных поколений.

Как уменьшить количество draw calls: от статического батчинга до SRP Batcher

Draw call — команда CPU к GPU «нарисуй это». Каждый вызов имеет overhead независимо от сложности геометрии. На мобильных 200–300 draw calls за кадр — граница. Цель: минимизировать их количество, объединяя геометрию с одинаковым материалом.

Static Batching объединяет неподвижные меши при сборке. Требование: флаг Static на объекте и одинаковый материал. Эффективен для статичного окружения, но увеличивает потребление памяти — объединённый меш хранится отдельно. В сценах с тысячами статичных объектов следите за памятью через Memory Profiler.

Dynamic Batching объединяет меши в рантайме с жёсткими ограничениями: меньше 900 вертексных атрибутов на меш, одинаковый материал и масштаб. На практике эффективен только для мелких объектов (партиклы, UI). В URP по умолчанию отключён — его вытеснил SRP Batcher.

SRP Batcher — не классический батчинг, а оптимизация CPU-overhead при подготовке draw calls. Вместо того чтобы каждый кадр заново загружать uniform-данные шейдера, SRP Batcher кэширует их в GPU-памяти и обновляет только изменившиеся. Draw calls остаются прежними по количеству, но каждый занимает меньше времени CPU — иногда в 2–3x по CPU-времени рендера. Требование: шейдер должен быть совместим с SRP Batcher (декларировать per-object свойства в UnityPerDraw CBUFFER). Стандартные URP Lit/Unlit шейдеры совместимы. Кастомные — проверяем в Inspector материала: SRP Batcher compatible: Yes/No. Для включения убедитесь, что в URP Asset опция SRP Batcher активна, а для кастомных шейдеров используйте макрос UNITY_INSTANCING_BUFFER и объявляйте per-object свойства в блоке CBUFFER_START(UnityPerDraw). После включения в Profiler должно снизиться время RenderLoop.Draw на CPU.

GPU Instancing — для множества копий одного меша с одним материалом (деревья, трава, NPC). Отправляет один draw call с массивом per-instance данных. Включается на материале: Enable GPU Instancing. Ограничение: все инстансы в одном batch должны иметь одинаковый материал и меш. Graphics.DrawMeshInstanced / Graphics.DrawMeshInstancedIndirect — для процедурного рендеринга без GameObject overhead.

Выбор метода зависит от сценария: Static Batching подходит для статичного окружения, SRP Batcher выигрывает в проектах с множеством уникальных материалов, GPU Instancing незаменим для массовых объектов (лес, толпа), Dynamic Batching — только для мелких и редких случаев. На практике мы комбинируем всё, начиная с профилирования.

Метод Тип объектов Влияние на CPU Влияние на GPU Потребление RAM
Static Batching Статичные Умеренное снижение Без изменений Увеличивается
Dynamic Batching Мелкие (≤900 вертексов) Снижение Без изменений Без изменений
SRP Batcher Любые (совместимые шейдеры) Значительное снижение Без изменений Без изменений
GPU Instancing Копии одного меша Минимальное Значительное снижение Незначительно

Подробнее о технологии — на Wikipedia.

Почему оптимизация памяти критична для мобильных игр?

Мобильные платформы — жёсткие ограничения по RAM. iOS убивает приложение при превышении памяти без предупреждения (memory pressure kill). Android — аналогично, но с onLowMemory callback. Целевые бюджеты: iOS — <1 GB для современных устройств, <512 MB для поддержки iPhone 8/X; Android — <800 MB для широкой совместимости (ОС занимает 400–600 MB). Типичная экономия после нашей оптимизации — 30–50% оперативной памяти при сохранении качества.

Addressables и Asset Bundles: как не превысить бюджет

Загружать всё сразу при старте — неприемлемо для больших проектов. Addressables (надстройка над Asset Bundles) — система адресуемой асинхронной загрузки ассетов. Явная выгрузка: Addressables.ReleaseInstance / Addressables.Release. Addressables не выгружают ассеты автоматически при уничтожении объекта. Типичная ошибка: Addressables.InstantiateAsync в цикле без Release — память растёт до краша. Reference counting: ассет выгружается только когда все его handles освобождены. Архитектурный паттерн: сервис/менеджер держит handle загруженного ассета, освобождает при переходе между сценами.

Groups и Bundle Strategy: группируем ассеты по логике загрузки. Например, все ассеты одного уровня — в один bundle, шаренные (UI, шрифты) — в отдельную группу с Prevent Updates. Такая стратегия экономит до 30% памяти.

Texture Memory: откуда берётся 70% занимаемого объёма

Текстуры — основной потребитель памяти. Анализ через Memory Profiler: вкладка All Of MemoryTexture2D — сразу виден список самых тяжёлых текстур. На практике мы находим текстуры с завышенным Max Size (4096 для мобильной иконки — типичная ошибка). Меры: Mipmap для 3D-текстур (включить), для UI (выключить); Streaming Mipmaps для open world — загружает mip-уровни по мере приближения камеры. Крассовская проблема: текстуры, на которые ссылаются неиспользуемые Materials, остаются в памяти — Memory Profiler покажет reference chain. Удаляйте лишние материалы. После замены всех текстур формата RGBA32 на ASTC 6×6 на Android экономия достигает 60% без потери качества.

GC Allocations: как устранить фризы в Hot Path

C# garbage collector в Unity — stop-the-world. Если за кадр аллоцировано много heap-памяти, GC-пауза вызывает видимый фриз. Цель: нулевые аллокации в hot path (Update, FixedUpdate, рендер). Типичные источники: string конкатенация в Update (заменяем на StringBuilder), LINQ в hot path (ручные циклы с предаллоцированными списками), GetComponent<T>() каждый кадр (кэшируем в Awake/Start), boxing value types при передаче в object параметры. После профилирования с помощью Unity Profiler мы снижаем аллокации в hot path на 95% — фризы исчезают.

LOD и Culling: как отсечь лишние 40% draw calls

LOD Group — переключение на упрощённую геометрию при удалении объекта от камеры. Стандарт для 3D окружения: LOD0 (100%), LOD1 (30–50% треугольников), LOD2 (10–15%), Culled. Для мобильных порог Culled ставим агрессивнее — меньше рисуем за кадр.

Occlusion Culling — Unity не рендерит объекты за стенами. Требует запечённые occlusion данные. Для indoor сцен снижает draw calls на 20–40%.

Frustum Culling работает автоматически — объекты вне FOV камеры не рендерятся. Но draw call на проверку всё равно происходит. Для сцен с тысячами объектов — кастомный spatial partitioning (Quadtree, Octree). В одном из проектов внедрение окклюзионного калинга и LOD снизило общее количество draw calls с 2800 до 450 на Android.

Оптимизация VR: как удержать 72 FPS на Quest 3

VR — отдельный класс задач. Фреймрейт 72/90 Hz нельзя нарушать, иначе motion sickness. Дополнительно к стандартным методам: Single Pass Instanced Rendering — рендер для обоих глаз за один проход (снижает draw calls в 2 раза); Fixed Foveated Rendering (Quest) — снижение разрешения на периферии; Late Latching (Quest 3) — обновление позиции контроллера максимально поздно перед рендером; Dynamic Resolution в URP/HDRP — автоматическое снижение разрешения рендера при просадке fps. Для Quest профилируем через OVR Metrics Tool — показывает CPU/GPU time прямо в гарнитуре. После применения этих методов частота кадров на Quest 2 стабилизируется на 72 FPS даже в сценах с 1.5 млн полигонов.

Что входит в работу по оптимизации: этапы и сроки

Мы предлагаем комплексную услугу «под ключ»:

  1. Профилирование на целевых устройствах — сбор метрик (FPS, draw calls, память, GC). Анализируем 5–7 основных сценариев за 3–5 рабочих дней.
  2. Отчёт с приоритетами — какие проблемы критичны, какие можно отложить. Приоритеты выставляем на основе влияния на игровой опыт.
  3. Внедрение оптимизаций — батчинг, LOD, Addressables, шейдеры, occlusion culling. Средний цикл внедрения — 2–4 недели.
  4. Повторное профилирование — замер улучшений. Типичный прирост FPS — 30–60% на мобильных устройствах.
  5. Документация — рекомендации по поддержке и дальнейшей разработке.
  6. Обучение команды — как не допустить регресса. Проводим воркшопы по профилированию и оптимизации.

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