Верстка інтерфейсів ігор у Unity UI (uGUI)
Unity UI (uGUI)—система, що працює через Canvas, LayoutGroup та EventSystem. При правильному налаштуванні вона дає стабільний, передбачуваний інтерфейс з мінімальними draw calls. При неправильному—200+ draw calls на одному екрані інвентаря та Canvas.ForceUpdateCanvases() виклики, які висять на 8–12 мс раз у кілька кадрів.
Більшість проблем з продуктивністю uGUI закладені в архітектурі Canvas, а не в кількості елементів.
Canvas та батчинг: де теряються draw calls
Canvas збирає всі UI-елементи у batch—групу об'єктів, які рендеряться одним draw call. Умова батчингу: однаковий матеріал + однакова текстура (Sprite Atlas) + однакова глибина без перекриттів з чужорідними елементами.
Три речі руйнують батчинг. Перше—змішування джерел текстур: іконка з Атласу A, кнопка з Атласу B, текст через TextMeshPro з власним атласом шрифту. Кожне переключення текстури—новий draw call. Рішення: всі візуальні елементи одного екрана—в один Sprite Atlas. TextMeshPro-тексти групуємо окремо, вони завжди будуть своїм batch'ем, але хоча б одним.
Друге—вкладені Mask компоненти. Кожен Mask у uGUI додає 2 stencil-проходи та розриває batch всього, що знаходиться всередину та зовні маски. Для прокручуваних списків неминуче, але для статичних елементів з «обрізаними кутами» краще намалювати текстуру з потрібною формою, ніж додавати Mask.
Третє—неправильний порядок Sibling Index при перекритті елементів. Якщо Image A та Image B перекриваються і вони з одного атласу, batch все одно розривається. Unity batches працюють тільки якщо немає перекриттів між елементами з різних атласів. Це означає, що z-порядок UI-елементів впливає на батчинг так само, як і текстурний атлас.
Діагностика: Frame Debugger → кнопка Enable → розворачиваємо рендер-дерево до секції UI → лічимо кількість draw calls на UI. Гарна цифра для одного HUD—5–15. Більше 50—щось пішло не так.
RectTransform: правила, які спасають від болю
RectTransform—основа позиціонування в uGUI. Якорі (Anchors) та Pivot—не те саме. Якір визначає, до якої точки батьківського контейнера прив'язаний елемент. Pivot—точка обертання та масштабування самого елемента.
Типова помилка: якорі встановлені в лівий верхній кут, а елемент повинен перебувати в центрі екрана незалежно від роздільності. При зміні роздільності елемент уїжджає. Коректно: якір → center/middle, позиція через anchored position = (0, 0). Тоді елемент залишається в центрі на будь-якій роздільності.
Для адаптації до різних роздільностей використовуємо Canvas Scaler. Режим Scale With Screen Size з Reference Resolution 1920×1080 та Match = 0.5 (середня між Width та Height)—робоча база для більшості проектів. Для мобільних з різними співвідношеннями сторін Match краще піднести до 0.8–1.0 на користь Height, інакше на iPhone з співвідношенням 19.5:9 UI розпливеться по горизонталі.
ContentSizeFitter + VerticalLayoutGroup—комбінація для динамічних списків. ContentSizeFitter розтягує контейнер під вміст, VerticalLayoutGroup розставляє дочірні елементи. Проблема: кожна зміна в ієрархії викликає Layout Rebuild—перерахунок позицій всіх елементів. Для ScrollView із сотнями елементів—катастрофа. Рішення—Virtual Scroll List (pool-based): тримаємо у пам'яті тільки видимі елементи, остачу повертаємо в пул при прокруцюванні. Потребує ручної реалізації, але скорочує кількість активних UI-об'єктів зі сотень до 10–15.
TextMeshPro: шрифти та продуктивність
TextMeshPro—стандарт для тексту в Unity UI. Використовує Signed Distance Field (SDF) рендеринг: шрифт виглядає чітко на будь-якому масштабі без растру. Для кожної мови потрібен свій Font Asset з потрібними глифами.
Проблема при локалізації: кирилиця, китайська та японська потребують різних Font Assets. При змішуванні мов на екрані—кілька Font Assets, кілька атласів шрифтів, кілька draw calls тільки на текст. Рішення—Fallback Font Asset: основний шрифт для латиниці з зареєстрованим Fallback на кириличний/CJK шрифт. TextMeshPro автоматично використовує fallback для відсутніх глифів, і весь текст залишається в межах одного-двох draw calls.
Atlas Population Mode: Dynamic (за замовчуванням) додає глифи в атлас по мірі зустрічання в тексті—добре для dev-білдів, погано для продакшну (можливі мікрофризи при першому рендері нового глифу). Для релізу використовуємо Static з попередньо заповненим атласом через Font Asset Creator.
Кейс: 200 draw calls на екрані інвентаря
На проект прийшла скарга на 15% FPS просадку при відкритті інвентаря на мобільних пристроях. Frame Debugger показав 214 draw calls для одного екрана з 48 слотами предметів. Причина: кожен слот був окремим Prefab з іконкою з індивідуального PNG (не атлас), TextMeshPro-кількістю, фоновою рамкою з іншого PNG, та Outline-компонентом (який у uGUI генерує додатковою mesh).
Рішення: упаковка всіх іконок в два Sprite Atlases (предмети та UI-хром окремо), заміна TextMeshPro Outline на SDF Outline в налаштуваннях матеріалу TMP, додавання Virtual Scroll List до сітки предметів. Результат: 22 draw calls для того ж екрана. FPS на мобільних пристроях повернувся до норми.
Процес верстки
Розпочинаємо з отримання Figma-макетів з вказанням розмірів, відступів та правил адаптації. Визначаємо структуру Canvases та атласів. Верстаємо базовий layout на RectTransforms, підключаємо шрифти та текстури. Налаштовуємо EventSystem та навігацію для геймпада/клавіатури. Тестуємо на кількох роздільностях через Game View з кастомними роздільностями. Профілюємо через Frame Debugger.
| Обсяг | Строки |
|---|---|
| 1–3 екрани (прості, без анімацій) | 2–5 днів |
| Повний UI-комплект інді-проекту (10–15 екранів) | 3–6 тижнів |
| Складні екрани з virtual scroll, drag & drop | 1–3 тижні за систему |
| Мультиплатформенна адаптація існуючого UI | 1–4 тижні |
Вартість визначається після аналізу Figma-макетів та технічних вимог.





