Програмування логіки відображення UI в іграх

Уявіть: гравець натискає кнопку в меню, а інтерфейс зависає на секунду — затримка через невдало написану прив'язку даних. Або при перетягуванні предмета в інвентарі ghost-іконка не слідує за курсором, тому що RectTransformUtility неправильно спроектував координати. Такі баги не впадають в очі на ран

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

Інші послуги студії

VR/AR/MR застосунки на замовлення

Вражайте клієнтів і навчайте команду у віртуальній реальності

Розробка ігор на Unity

Від ідеї до релізу — ігри, які запам'ятовуються

3D-моделювання та анімація

Оживимо ваш продукт в об'ємній графіці та анімації

VR-тренажери промислового обладнання

Тренуємо операторів на техніці без ризику і простою

AR-інструкції для виробництва

Покрокові підказки прямо на обладнанні — без паперу

Safety-тренажери

Відпрацювання НС і техніки безпеки без виходу на об'єкт

VR/AR-тренінги

Навчаємо персонал сервісу, адаптації та soft skills у VR

Навчальні вікторини

Перевірка знань у форматі гри — легко і без стресу

Корпоративні відеоінструкції

Зрозумілі ролики для навчання співробітників і клієнтів

Гейміфікація бізнес-процесів

Мотивуємо команду через ігрові механіки в KPI та HR

Застосунки для інфокіосків

Інтерактивні екрани для магазинів, стендів і офісів

VR/AR-інсталяції

Wow-ефект для брендів на виставках, івентах і в шоу-румах

Віртуальні виставки та музеї

Ваша експозиція доступна з будь-якої точки світу — 24/7

Event-квести та брендовані ігри

Незабутні ігри для конференцій та клієнтських івентів

Часті запитання

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

  • image_games_mortal_motors_495_0.webp
    Розробка гри для компанії Mortal Motors
    1526
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Покрокова стратегія у фентезі сеттингу With Fire And Sword
    1030
  • image_games_second_team_604_0.webp
    Розробка ігри для компанії Second term
    657
  • image_games_phoenix_ii_606_0.webp
    3D-анімація – тизер для гри phoenix 2.
    738
  • image_training-quizzes_kids_shopping_quiz_614_0.webp
    Навчальна вікторина для дітей «Покупки в магазині»
    142

Уявіть: гравець натискає кнопку в меню, а інтерфейс зависає на секунду — затримка через невдало написану прив'язку даних. Або при перетягуванні предмета в інвентарі ghost-іконка не слідує за курсором, тому що RectTransformUtility неправильно спроектував координати. Такі баги не впадають в очі на ранніх етапах, але на етапі збору зворотного зв'язку від тестувальників перетворюються на головний біль. Логіка відображення UI — це не просто візуал, а зв'язка патернів, подій та асинхронних операцій, яка займає до 30% часу розробки всього інтерфейсу. За 5 років ми реалізували UI-логіку для 40+ проєктів — від мобільних казуалок до PC-екшенів, і накопичили набір перевірених рішень, які скорочують час розробки на 20-40% та знижують GC Alloc на 60-80%. Оцініть бюджет вашого UI за 30 хвилин консультації — зв'яжіться з нами.

Програмування логіки відображення графіки інтерфейсу

Головний архітектурний вибір — як UI дізнається про зміни в стані гри. Три основні підходи:

Observer / Event System — UI підписується на події ігрової системи: HealthSystem.OnHealthChanged += UpdateHealthBar. Слабкий зв'язок, можна легко переключати UI-реалізації. Проблема: при знищенні об'єкта до відписки — NullReferenceException. Обов'язково відписуватися в OnDisable() або OnDestroy(). Це реалізація Observer pattern.

MVP (Model-View-Presenter) — Presenter відповідає за зв'язок між даними (Model) і відображенням (View). View не знає про ігрову логіку, тільки про свої візуальні компоненти. Presenter знає обидві сторони. Добре масштабується, добре тестується. Типова реалізація в Unity — абстрактний клас BaseView з методами Show(), Hide(), Bind(IModel model), і конкретні Presenter'и під кожен екран.

ScriptableObject-based подійна система — всі події як ScriptableObject з методом Raise() і списком слухачів. Популяризовано Ryan Hipple на Unite кілька років тому. Зручно для невеликих команд, легко дебажити в Inspector. Мінус: при великій кількості подій стає складно керувати залежностями.

Для більшості проєктів рекомендую MVP з EventSystem для крос-модульної комунікації. Це дає читабельний код, передбачувану поведінку та хорошу тестованість UI без запуску сцени.

Як керувати станами екранів?

Screen Manager — типова система, яка контролює, який екран відкрито, і керує переходами. Мінімальна реалізація: стек екранів (для Back navigation), словник screenId → IScreen, методи Push(), Pop(), Replace().

Для мобільних платформ Android Back Button повинен коректно оброблятися через Application.exitCancellationToken або через Input.GetKeyDown(KeyCode.Escape) — при натисканні повинен закриватися верхній екран у стеку, а не виходити з гри. Це те, що часто забувають при розробці та помічають при здачі в Google Play.

CanvasGroup — інструмент для керування видимістю та інтерактивністю групи елементів (див. документацію Unity). canvasGroup.alpha = 0 приховує візуально, але елементи продовжують отримувати події. Обов'язково також виставляти canvasGroup.interactable = false та canvasGroup.blocksRaycasts = false при приховуванні — інакше невидимі кнопки перехоплюють кліки крізь них.

Кейс з нашої практики: інвентар з drag & drop — програмування логіки відображення

Задача: drag & drop перетягування предметів між слотами інвентаря з підтримкою геймпада. На мишці — IBeginDragHandler, IDragHandler, IEndDragHandler. На геймпаді — зовсім інша логіка: курсорний режим навігації з вибором джерела та цілі через кнопки.

Реалізація drag & drop в uGUI вимагає створення ghost-об'єкта — копії перетягуваної іконки, яка слідує за курсором через RectTransformUtility.ScreenPointToLocalPointInRectangle(). Ghost повинен знаходитися в окремому Canvas поверх усього іншого (окремий Canvas з Sort Order вище основного) — інакше іконка буде перекриватися іншими елементами при перетягуванні.

Для геймпада реалізували окремий режим: перше натискання A вибирає слот (підсвічується selected state), навігація D-pad переміщує віртуальний курсор між слотами, друге натискання A завершує перетягування. Два режими керування — два різні кінцеві автомати в одному InventoryController.

Як async/await спрощує UI-логіку і в чому підводний камінь?

Сучасна Unity-розробка все активніше використовує async/await замість корутин. Для UI-логіки це особливо зручно при роботі з мережевими запитами, завантаженням даних, очікуванням анімації перед переходом екрана.

Головна пастка async в Unity: операції продовжуються навіть після знищення об'єкта. await Task.Delay(2000) в методі кнопки — і через 2 секунди код продовжує виконуватися, звертаючись до вже знищених компонентів. NullReferenceException гарантовано. Рішення: перевірка this == null після кожного await (Unity перевизначає оператор == для UnityEngine.Object), або використання CancellationToken, який скасовується в OnDestroy.

Другий нюанс: async-метод, кинутий без await (fire-and-forget), не обробляє виключення. LoadUserData() без await — виключення всередині методу просто проковтується, користувач бачить порожній екран, логів немає. Для fire-and-forget методів обов'язково додаємо глобальний обробник через TaskScheduler.UnobservedTaskException або обгортаємо виклик в try-catch всередині самого методу.

UniTask (Cysharp/UniTask) — суттєво кращий варіант, ніж стандартний Task для Unity. Працює без heap allocation для більшості операцій, інтегрується з PlayerLoop Unity, підтримує UniTask.Delay з прив'язкою до PlayerLoopTiming (Update, FixedUpdate, LateUpdate) та коректно обробляє cancellation при знищенні об'єкта через destroyCancellationToken. На проєктах з активним використанням async це знижує GC Alloc на 60–80% порівняно з System.Threading.Tasks — тобто в 2–3 рази ефективніше.

Як покроково реалізувати Screen Manager для навігації

  1. Визначте інтерфейс IScreen з методами Show() та Hide().
  2. Створіть ScreenManager як синглтон або сервіс в DI.
  3. Зберігайте стек екранів для підтримки Back.
  4. Реалізуйте методи Push() (відкрити новий екран, приховавши поточний) та Pop() (закрити поточний, повернути попередній).
  5. Для мобільних пристроїв підпишіться на кнопку Back та викликайте Pop().
  6. Додайте Replace для випадків, коли не потрібно повертатися до попереднього екрана.

Порівняння підходів до зв'язку UI та логіки

Підхід Зв'язність Складність реалізації Тестованість Продуктивність
Event System Низька Середня Середня Висока
MVP Середня Висока Висока Висока
ScriptableObject Events Низька Низька Низька Середня

Логіка відображення даних: типові проблеми

Оновлення UI кожен кадр — антипатерн. healthBar.fillAmount = player.health / player.maxHealth в Update() працює, але перестворює mesh для Image компонента кожен кадр навіть якщо значення не змінилося. Правильно: оновлювати тільки при зміні даних через event або property з setter.

Рядкова конкатенація в Update — гірше. levelText.text = "Level: " + player.level створює новий рядок кожен виклик, провокуючи GC Allocations. Використовуємо string.Format() або StringBuilder для часто оновлюваних текстів. У TextMeshPro є SetText(string, float) — перевантаження з float-аргументом, яке форматує число без GC allocation.

Z-fighting кнопок: два Button'и з однаковим Sort Order в одному Canvas, один поверх іншого. EventSystem надсилає подію на верхній за ієрархією, але якщо Raycast Target увімкнено в обох — кліки можуть провалюватися. Перевіряємо через UI Debugger (правий клік в EventSystem → UI Debug).

Відображення Loading State: при очікуванні відповіді від сервера UI повинен блокувати повторні натискання. Типова помилка — просто показати Spinner і забути вимкнути кнопку. Правильно: блокуємо CanvasGroup.interactable = false для всього екрана + показуємо Loading Overlay + в finally блоці async-методу відновлюємо interactable = true. Інакше при повільному з'єднанні користувач встигає натиснути кнопку кілька разів.

Що входить в роботу

  • Архітектурне рішення патерну зв'язку UI з ігровою логікою
  • Реалізація Screen Manager та навігації
  • Логіка кожного екрана з юніт-тестами Presenter'ів
  • Інтеграція з ігровими системами (події, дані)
  • Налаштування CanvasGroup, обробка введення (миша, геймпад)
  • Вирішення проблем з async/await та корутинами
  • Тестування на цільових пристроях та профілювання

Процес та терміни

Розробка починається з архітектурного рішення: вибір патерну зв'язку з ігровою логікою, визначення Screen Manager архітектури, узгодження інтерфейсів між UI та ігровими системами. Потім — реалізація базової інфраструктури (Screen Manager, Base View, Event Bus). Далі — поекранна реалізація з юніт-тестами для Presenter'ів. Фінал — інтеграційне тестування на пристроях.

Задача Терміни
Логіка 1 екрана (відображення даних, кнопки) 1–3 дні
Screen Manager + навігація для всього проєкту 3–7 днів
Складна система (інвентар, drag & drop, геймпад) 1–2 тижні
Повна UI-логіка інді-проєкту (10–15 екранів) 4–10 тижнів

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