Послуги з оптимізації продуктивності ігор

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

Від імерсивних застосунків до ігрових світів і 3D-сцен

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

Відвідати персоналізований сайт
Показано 10 з 10 послугУсі 242 послуг
Оптимізація графіки під мобільні платформи
Складна
від 3 робочих днів до 2 тижнів
Оптимізація графіки під VR/AR пристрої
Складна
від 3 робочих днів до 2 тижнів
Налаштування рівнів деталізації (LOD) для графіки
Середня
від 1 робочого дня до 1 тижня
Оптимізація відрисовки (Draw Calls) в іграх
Складна
від 2 робочих днів до 2 тижнів
Оптимізація використання оперативної пам'яті в іграх
Складна
від 2 робочих днів до 2 тижнів
Профілювання CPU та GPU ресурсів ігор
Складна
від 1 робочого дня до 1 тижня
Часті питання

Наші компетенції

Які етапи розробки гри?

Останні роботи

  • image_games_mortal_motors_495_0.webp
    Розробка гри для компанії Mortal Motors
    683
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Покрокова стратегія у фентезі сеттингу With Fire And Sword
    862
  • image_games_second_team_604_0.webp
    Розробка ігри для компанії Second term
    491
  • image_games_phoenix_ii_606_0.webp
    3D-анімація – тизер для гри phoenix 2.
    533

Оптимізація продуктивності

Проект запускається на топових девайсах розробника без запитань. На mid-range Android 2020 року — 20 fps та перегрів через 5 хвилин. На iPhone 11 — стабільні 60, але на iPhone XR — просадки в важких сценах. Це стандартна ситуація, коли оптимізація не закладалася в архітектуру з самого початку, а робиться «потім» — що завжди дорожче та болісніше.

Інструменти профілювання

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

Інструмент Призначення
Unity Profiler CPU/GPU час по системам, GC allocations, аудіо
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, fillrate), або в пам'яті (GC allocations, texture streaming). Лікування різне.

Оптимізація Draw Calls: батчинг в деталях

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

Static Batching

Об'єднує статичні (нерухомі) меші в єдиний великий меш при збиранні або на старті сцени. Вимоги:

  • Static флаг на об'єкті (або хоча б Batching Static)
  • Однаковий матеріал

Плюси: нема CPU-overhead у рантаймі, працює на всіх платформах. Мінуси: збільшує споживання пам'яті (об'єднаний меш зберігається окремо від оригіналу) та час загрузки сцени. Для сцен із тисячами статичних об'єктів — обережно з пам'яттю, дивимось через Memory Profiler.

Dynamic Batching

Об'єднує меші у рантаймі для кожного кадру. Вимоги жорсткіші:

  • Менше 900 вертексних атрибутів на меш (обмеження Unity)
  • Однаковий матеріал
  • Однаковий масштаб (або не-негативний масштаб по одній осі)

На практиці Dynamic Batching ефективен тільки для дрібних об'єктів (партиклі, UI-елементи, дрібний дебрис). Для персонажів та оточення зазвичай не підходить через обмеження по вертексам. У URP Dynamic Batching за замовчуванням відключений — його витіснив SRP Batcher.

SRP Batcher

SRP Batcher — не класичний батчинг геометрії, а оптимізація CPU-overhead при підготовці draw calls. Замість того щоб кожен кадр заново загружати uniform-дані шейдера (матриці, властивості матеріалу), SRP Batcher кешує їх у GPU-пам'яті й оновлює тільки змінені.

Результат: draw calls залишаються по кількості попередніми, але кожен займає менше часу CPU. У сценах із великою кількістю унікальних матеріалів SRP Batcher дає відчутний прирост — іноді 2-3x по CPU-часу рендера.

Вимога: шейдер повинен бути сумісний із SRP Batcher — оголошувати всі per-object властивості у UnityPerDraw CBUFFER. Стандартні URP Lit/Unlit шейдери сумісні. Кастомні шейдери — перевіряємо в Inspector: покаже SRP Batcher compatible: Yes/No.

GPU Instancing

Для множини копій одного й того ж меша з одним матеріалом (дерева, трава, NPC одного типу, снаряди). GPU Instancing відправляє один draw call із масивом per-instance даних (матриці трансформації, колір) — GPU малює всі копії за один раз.

Включається на матеріалі: Enable GPU Instancing checkbox. У шейдері — підтримка через UNITY_INSTANCING_BUFFER (стандартні URP шейдери підтримують). Обмеження: всі інстанси в одному batch повинні мати однаковий матеріал та меш.

Graphics.DrawMeshInstanced / Graphics.DrawMeshInstancedIndirect — для процедурного рендерингу інстансів без GameObject overhead (трава, партиклі, процедурний контент). Indirect-версія дозволяє формувати список інстансів на GPU через Compute Shader.

Порівняння підходів:

Метод Найкращий сценарій Обмеження
Static Batching Статичне оточення Пам'ять
SRP Batcher Багато унікальних матеріалів CPU overhead тільки
GPU Instancing Багато копій одного об'єкту Однаковий матеріал/меш
Dynamic Batching Дрібні об'єкти у URP Вертексне обмеження

Оптимізація пам'яті для мобільних

Мобільні платформи — жорсткі обмеження по RAM. iOS вбиває додаток при перевищенні пам'яті без попередження. Android — аналогічно, але з onLowMemory callback. Цільові бюджети:

  • iOS: < 1 GB для сучасних пристроїв, < 512 MB для підтримки iPhone 8/X
  • Android: < 800 MB для широкої сумісності, враховуючи що Android сам займає ~400-600 MB

Addressables та Asset Bundles

Загружати все відразу при старті — неприйнятно для великих проектів. Addressables (надбудова над Asset Bundles) — система адресуємої асинхронної загрузки ассетів.

Ключові принципи:

Явна виконання: Addressables.ReleaseInstance / Addressables.Release. Addressables не виганяють ассети автоматично при знищенні об'єкту. Типова помилка: Addressables.InstantiateAsync у циклі без Release — пам'ять зростає до краху.

Reference counting: ассет виганяється тільки коли всі його handles звільнені. Архітектурний паттерн: сервіс/менеджер тримає handle завантаженого ассету, звільняє при переході між сценами або явному виклику.

Groups та Bundle Strategy: групуємо ассети по логіці загрузки:

  • Pack Together — всі ассети групи в одному bundle (загрузка одним запросом)
  • Pack Separately — кожен ассет у своєму bundle (гранулярна загрузка)
  • Pack Together by Label — по мітках (гнучкий варіант)

Для рівнів: всі ассети одного рівня в одному bundle. Шаренні ассети (загальні текстури UI, шрифти) — в окремій групі з Prevent Updates для стабільного кешу.

Texture Memory

Текстури — основний потребувач пам'яті в більшості ігор. Аналіз через Memory Profiler: вкладка All Of MemoryTexture2D — відразу видно список найважчих текстур.

Практичні заходи:

  • Mipmap: для 3D-текстур — включити, для UI — виключити (Advanced > Generate Mip Maps: false). UI-текстури рендерються у фіксованому просторі, mipmap тільки тратить пам'ять
  • Max Size: перевірити, не завищен ли Max Size в Import Settings. 4096 для мобільної іконки — типова помилка
  • Крос-посилання: текстури, на які посилаються невикористані Materials у пам'яті — Memory Profiler покаже reference chain
  • Streaming Mipmaps: для open world — включити Texture Streaming у Quality Settings. Загружає mip-рівні по мірі наближення камери

GC Allocations

C# garbage collector у Unity — stop-the-world. Якщо за кадр алоцировано багато heap-пам'яті, GC-пауза викличе помітний фриз. Мета: нульові аллокації у hot path (Update, FixedUpdate, рендер).

Типові джерела аллокацій, які знаходимо в Profiler:

  • string конкатенація в Update ("Score: " + scoreStringBuilder або string.Format)
  • LINQ у hot path (Where, Select, ToList → ручні цикли з предаллоцированими списками)
  • GetComponent<T>() кожен кадр → кешувати в Awake/Start
  • new Vector3() та інші value types у деяких паттернах — перевіряти Profiler
  • Boxing value types при передачі в object параметри

LOD та Culling

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

Occlusion Culling — Unity не рендерить об'єкти за стінами та перешкодами. Вимагає запічені occlusion дані (Window > Rendering > Occlusion Culling > Bake). Для відкритих просторів ефект мінімален, для indoor сцен — істотен.

Frustum Culling працює автоматично — об'єкти поза FOV камери не рендерються. Але draw call на перевірку все одно відбувається. Для сцен із тисячами об'єктів — кастомний spatial partitioning (Quadtree, Octree) для прискорення culling-тесту.

Оптимізація VR

VR — окремий клас задач. Фреймрейт 72/90 Hz неможливо нарушати, інакше motion sickness. Додатково до стандартних методів:

  • Single Pass Instanced Rendering — рендер для обох очей за один прохід (див. VR-розділ)
  • Fixed Foveated Rendering (Quest) — зниження розрізнення на периферії
  • Late Latching (Quest 3) — оновлення позиції контролера максимально пізно перед рендером, знижує perceived latency
  • Dynamic Resolution у URP/HDRP — автоматичне зниження розрізнення рендера при просадці fps

Для Quest профілюємо через OVR Metrics Tool — показує CPU/GPU time прямо в гарнітурі у рантаймі, що зручніше за профілювання через USB.