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

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

Від імерсивних застосунків до ігрових світів і 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 МБ. Економія склала 56 МБ, що при вартості 1 ГБ RAM у $0.01 за 1 ГБ/год дає $0.56 економії на годину роботи пристрою. У цій статті ми розповімо, як правильно оптимізувати текстурні атласи для мобільних ігор.

Чому атласи — вузьке місце мобільної оптимізації?

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

Інша історія: атлас налаштований правильно, batching draw calls працює, але розмір 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 разів при майже непомітній втраті якості — це у 6 разів краще за співвідношенням якості до розміру.

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

PVRTC — формати для iOS (PowerVR GPU). Вимагає текстур квадратної форми зі стороною в ступінь двійки. Якщо атлас 1024×512 — PVRTC застосувати не можна без зміни розміру.

Офіційна документація Unity по форматах стиснення: https://docs.unity3d.com/Manual/class-TextureImporterOverride.html

Реальний кейс: казуальна гра-пазл, 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 пристроях різних поколінь. Аналізуємо 10–15 ключових ігрових сценаріїв за 3 робочих дні.

Як зменшити кількість draw calls?

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

Static Batching об’єднує нерухомі меші при збірці. Вимога: прапорець Static на об’єкті та однаковий матеріал. Ефективний для статичного оточення, але збільшує споживання пам’яті — об’єднаний меш зберігається окремо. Static Batching може знизити draw calls в 3 рази, але збільшує пам’ять на 20% порівняно з іншими методами. У сценах з тисячами статичних об’єктів слідкуйте за пам’яттю через Memory Profiler.

Dynamic Batching об’єднує меші в рантаймі з жорсткими обмеженнями: менше 900 вертексних атрибутів на меш, однаковий матеріал та масштаб. На практиці ефективний лише для дрібних об’єктів (партикли, UI). В URP за замовчуванням вимкнений — його витіснив SRP Batcher.

SRP Batcher — не класичний батчінг, а оптимізація CPU-overhead при підготовці draw calls. Замість того щоб кожен кадр заново завантажувати uniform-дані шейдера, SRP Batcher кешує їх у GPU-пам’яті та оновлює лише ті, що змінилися. Draw calls залишаються колишніми за кількістю, але кожен займає менше часу CPU — SRP Batcher дає приріст в 2–3 рази по 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% оперативної пам’яті при збереженні якості. Середня економія бюджету на етапі оптимізації — від $3,000 до $8,000 залежно від складності проекту, що підтверджується нашими кейсами.

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% — фризи зникають.

Як відсікти зайві 40% draw calls? LOD та Culling

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

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). Аналізуємо 10–15 ключових сценаріїв за 3–5 робочих днів.
  2. Звіт з пріоритетами — які проблеми критичні, які можна відкласти. Пріоритети виставляємо на основі впливу на ігровий досвід.
  3. Впровадження оптимізацій — батчінг, LOD, Addressables, шейдери, occlusion culling. Середній цикл впровадження — 2–4 тижні.
  4. Повторне профілювання — замір покращень. Типовий приріст FPS — 30–60% на мобільних пристроях.
  5. Документація — рекомендації по підтримці та подальшій розробці.
  6. Навчання команди — як не допустити регресу. Проводимо воркшопи з профілювання та оптимізації.

Завдяки запобіганню переробок на пізніх етапах замовник отримує значну економію. Отримайте консультацію — ми безкоштовно оцінимо ваш проект і покажемо потенціал оптимізації. Звертайтеся, щоб дізнатися точні терміни та вартість для вашого стеку.