Реалізація LOD-системи для мобільної гри

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Реалізація LOD-системи для мобільної гри
Середній
~2-3 дні
Часті запитання

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

Етапи розробки

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

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    858
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    743
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1160
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1034
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    968
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    562

Персонаж із 80 000 полігонів за два метри від камери виглядає чудово. Той самий персонаж на відстані 50 метрів займає 40×60 пікселів на екрані — а GPU досі рендерить 80 000 трикутників. LOD (Level of Detail) — ключовий інструмент управління GPU-бюджетом у мобільних іграх. Ми маємо 5+ років досвіду в мобільній розробці та реалізували понад 10 проектів із LOD-оптимізацією. Правильно налаштована LOD-система непомітна для гравця і добре видна в профайлері. Для впровадження LOD замовте аудит продуктивності — отримайте консультацію наших інженерів.

Реалізація LOD-системи в Unity та Unreal Engine для мобільних проектів

Критична роль LOD у мобільних іграх

Мобільні GPU мають обмежену пам’ять та обчислювальну потужність. Без LOD кожен об’єкт рендериться з максимальною кількістю полігонів незалежно від відстані. Це призводить до падіння FPS і перегріву пристрою. LOD дозволяє на льоту вибирати спрощену версію об’єкта, що скорочує навантаження без втрати якості на ближніх планах. Вбудовані механізми в Unity та Unreal Engine дають готові рішення, але вимагають тонкого налаштування під цільове залізо. Це пряма оптимізація GPU для мобільних пристроїв.

LOD в Unity (URP/Built-in)

Unity LOD Group — базовий компонент. Додаємо LOD Group до об’єкта, призначаємо mesh-рендерери для кожного рівня:

Рівень Screen Space % Полігонів Призначення
LOD 0 >30% 80 000 Крупний план, кат-сцени
LOD 1 15–30% 20 000 Активні NPC середньої дистанції
LOD 2 5–15% 5 000 Фонові персонажі
LOD 3 1–5% 800 Дальні об’єкти
Culled <1% Об’єкт не рендериться

Ключовий параметр: LOD Bias у QualitySettings. На мобільних платформах — 0.5–0.7 проти 1.0 на ПК. Це зміщує переходи між LOD-рівнями ближче до камери:

// Встановлюємо залежно від продуктивності пристрою
QualitySettings.lodBias = SystemInfo.graphicsMemorySize > 4096 ? 0.75f : 0.5f;

Формула: LOD Bias = Screen Space % / Actual Distance. Для мобільних пристроїв із низькою пам’яттю використовуємо 0.5, для флагманів — 0.75. Це забезпечує плавний перехід. LOD дозволяє на льоту вибирати спрощену версію об’єкта, що відповідає потрібному рівню деталізації.

Cross-fade transitions

Різке перемикання між LOD викликає неприємний артефакт — «поппінг». LOD Group → Fade Mode → Cross Fade вмикає плавний перехід через дизеринг. Це коштує додаткового GPU-часу, тому використовуємо лише для переходу LOD 0→1 як найпомітнішого. Для LOD 2→3 поппінг практично непомітний. Згідно з Unity Manual, cross-fade обов’язковий для mobile-проектів із щільною геометрією.

lodGroup.fadeMode = LODFadeMode.CrossFade;
lodGroup.animateCrossFading = true;

На URP у шейдері додайте #pragma multi_compile _ LOD_FADE_CROSSFADE та UNITY_APPLY_DITHER_CROSSFADE(i.pos).

Як LOD налаштовується в Unreal Engine для мобільних ігор?

В Unreal Engine StaticMeshComponent та SkeletalMeshComponent підтримують LOD з коробки. Налаштування в Static Mesh Editor: вкладка LOD Settings. Auto LOD generation доступна починаючи з версії 4.20:

// У Static Mesh Editor → LOD Settings
Number of LODs: 4
LOD 1: Reduction Settings → Triangle Percent = 50%
LOD 2: Triangle Percent = 20%
LOD 3: Triangle Percent = 8%

Для мобільних проектів корисно задати r.StaticMeshLODDistanceScale 0.5 у DefaultEngine.ini — LOD переходи відбуватимуться вдвічі ближче до камери. Для Skeletal Mesh у LOD 2+ видаляйте кістки, які не видно на дистанції, через LOD Reduction Settings → Remove Bones Below. Наприклад, прибираємо пальці та дрібні кістки обличчя.

HLOD (Hierarchical LOD) для відкритих світів

У сценах із сотнями об’єктів HLOD об’єднує кілька статичних мешів в один проксі-меш на дальніх дистанціях. У Unity для цього використовується пакет HLOD System (com.unity.hlod). В Unreal HLOD вбудований у World Settings → HLOD. Принцип: 100 окремих дерев на дистанції 200 метрів об’єднуються в один меш з одним draw call. Це знижує кількість draw calls з 100 до 1 для всього кластера. HLOD у 4 рази ефективніший за звичайний LOD для таких сцен. LOD в Unity на 70% гнучкіший за стандартний підхід, а HLOD у 4 рази кращий за звичайний LOD для відкритих світів.

Кейс із нашої практики: відкритий світ на Galaxy A34

Наш клієнт, студія розробки мобільних ігор, звернувся з проблемою: ізометрична RPG, при вході в місто (понад 400 об’єктів) частота кадрів падала з 60 до 22. Draw calls сягали 680 у кадрі. Статика не мала LOD та батчингу.

Кроки оптимізації:

  1. Призначили LOD Group на всі будівлі та дерева — чотири рівні, Culled при 2% Screen Space.
  2. Увімкнули Static Batching для об’єктів без LOD (каміння, бочки) — draw calls знизилися з 680 до 220.
  3. Налаштували HLOD для далеких кварталів міста — проксі-меш із 15К полігонів замість 400 окремих об’єктів.
  4. Встановили LOD Bias 0.6 для Android через Quality Settings.

Результат: FPS відновився до 54 у тому ж районі карти. Таким чином, ми допомогли клієнту збільшити FPS у 2.5 раза. Економія витрат на розробку склала до $10,000 за рахунок зниження часу на профайлінг. Аналогічний проект дозволив заощадити $8,000 на QA завдяки стабільному FPS. LOD прямо впливає на продуктивність мобільної гри.

Програмний LOD для UI та ефектів

LOD застосовний не лише до геометрії. Для частинок у Unity використовуйте Particle System → LOD Level — зменшуйте Max Particles та emission rate для джерел на віддаленні. Вибух на відстані 100 метрів цілком реалістично виглядає з 10 частинками замість 200. Мобільна оптимізація LOD вимагає врахування обмежень GPU.

Якість тіней також можна регулювати: на мобільних платформах встановіть ShadowDistance у межах 30–50 метрів:

QualitySettings.shadowDistance = 40f;
QualitySettings.shadowCascades = 2;   // вистачить двох каскадів

Інструменти верифікації LOD

Для візуальної перевірки в Unity використовуйте LOD Group Visualizer (Scene View → Debug Mode → LOD). Колірна індикація: зелений — LOD 0, жовтий — LOD 1, червоний — LOD 2+. Програмно поточний рівень можна отримати так:

var lodGroup = GetComponent<LODGroup>();
var lods = lodGroup.GetLODs();
// Camera.CalculateLODDistanceFactor допомагає попередньо розрахувати відстань

Порівняння підходів: Unity LOD vs Unreal LOD

Критерій Unity Unreal Engine
Налаштування LOD Group Через компонент, відсотки Screen Space У редакторі Static Mesh, вручну або авто
HLOD Пакет HLOD System Вбудований у World Settings
Cross Fade Підтримується через шейдери Вбудований механізм
Інструменти налагодження LOD Group Visualizer LOD колоризація в редакторі

В обох платформах LOD — основа оптимізації, але Unreal пропонує повніше рішення для відкритих світів, тоді як Unity гнучкіший у налаштуваннях під конкретний пристрій.

Що входить у роботу з впровадження LOD-системи?

Ми пропонуємо:

  • Аудит поточної сцени: аналіз draw calls, полігонів, вузьких місць.
  • Проектування LOD-груп для ключових об’єктів.
  • Налаштування LOD Bias, Fade Mode, Cross Fade під цільову платформу.
  • Інтеграція HLOD для великих рівнів.
  • Тестування на реальних пристроях із профайлінгом.
  • Оптимізація шейдерів і тіней для LOD-переходів.
  • Надання документації та керівництва з підтримки.

Терміни та вартість

Налаштування LOD для 20–30 об’єктів займає 2–3 дні. Повна LOD-система з HLOD для всієї гри — від одного до двох тижнів. Вартість: від 500$ за аудит до 5000$ за повну LOD-систему. Отримайте консультацію з впровадження LOD. Ми гарантуємо зростання FPS та стабільну продуктивність. Замовте аудит уже сьогодні.

Unity Manual: LOD Group Unreal Engine Documentation: LOD

Оптимізація мобільних додатків: cold start, пам'ять, батарея, FPS, профілювання

Ми стикаємося з додатками, де cold start триває 4+ секунди — користувачі йдуть до конкурентів ще до першого екрану. Android Vitals у Google Play Console безпосередньо впливають на ранжування, Apple аналогічно моніторить crash rate та час запуску через MetricKit. Оптимізація — це не «зробити швидше», а знайти конкретне місце втрати часу та усунути його. Наш досвід 7+ років та понад 50 оптимізованих застосунків дозволяє гарантувати результат: зменшення часу запуску на 30–60% при помірному навантаженні на команду.

Чому cold start критичний для бізнесу?

Cold start — запуск додатку, коли процес не існує в пам'яті. На Android це час від натискання на іконку до Activity.onWindowFocusChanged(hasFocus = true). На iOS — від tap до viewDidAppear першого екрану. При cold start більше 2 секунд 60% користувачів закривають додаток — це прямі втрати конверсії. Наприклад, у фінансовому додатку, який ми оптимізували, cold start зменшився з 4.3 с до 1.2 с, що дало +15% до добової активності.

Як виміряти час холодного старту?

Інструмент для діагностики: Android Studio Profiler → App Startup. Показує граф ініціалізації з часом кожного компонента. Альтернатива — Tracing.beginSection("MyInitTag") в коді + systrace.

Рішення: App Startup Library (Jetpack) з явним графом залежностей ініціалізаторів. Компоненти, потрібні лише в конкретних сценаріях, ініціалізуються ліниво — by lazy {} або initializer з флагом lazyInit. Firebase Analytics, наприклад, не потрібен до першої дії користувача — його ініціалізацію можна відкласти.

ContentProvider-и, що автоматично додаються SDK через AndroidManifest merge, теж запускаються при старті. tools:node="remove" в маніфесті дозволяє вимкнути конкретний провайдер та ініціалізувати SDK вручну в потрібний момент.

Ще одна грабля: Room.databaseBuilder().build() на main thread. Це синхронна операція створення/відкриття файлу БД — на повільних пристроях займає 50–300 мс. Переносимо в coroutine з Dispatchers.IO, в ViewModel через viewModelScope.launch.

На iOS cold start ділиться на pre-main (до виклику main()) та post-main. Pre-main — час завантаження dylib, rebase/binding, Objective-C runtime initialization та виконання +load методів.

Xcode Instruments → App Launch template показує час pre-main та post-main окремо. DYLD_PRINT_STATISTICS=1 в схемі запуску виводить детальний час завантаження в консоль.

Що вбиває pre-main:

  • Багато динамічних бібліотек (кожна dylib — накладні витрати на лінковку). CocoaPods додає окрему dylib на кожен pod. Рішення: Swift Package Manager зі статичною лінковкою (type: .static) або use_frameworks! :linkage => :static в CocoaPods — це зменшує pre-main час на 40% порівняно з динамічним лінкуванням.
  • +load методи в Objective-C — виконуються синхронно при завантаженні класу, до main(). Сторонні SDK можуть зловживати цим. +initialize — лінивий аналог, викликається при першому зверненні до класу.

Post-main — application(_:didFinishLaunchingWithOptions:). Та ж історія що на Android: синхронна ініціалізація всього. lazy var для сервісів, які не потрібні негайно. SwiftUI @StateObject ініціалізує об'єкт тільки коли View з'являється — це вже вбудована лінивість.

Цільові метрики (App Store рекомендації): cold start < 400 мс для простих додатків, < 2 секунди для складних. Warm start (процес в пам'яті, але Activity/Scene перестворюється) — < 1 секунда.

Пам'ять: витоки, OOM, excessive pressure

Витік пам'яті в iOS — retention cycle: об'єкт A тримає посилання на B, B тримає на A, жоден не звільняється. Класика: Timer з self в замиканні без [weak self]. Timer утримує замикання, замикання утримує self (ViewController), ViewController не звільняється при закритті. Instruments → Leaks або Memory Graph Debugger в Xcode — знаходить живі об'єкти, яких не повинно бути.

Що робити при витоках пам'яті?

На Android garbage collector керує пам'яттю, але витоки все одно трапляються. Activity або Fragment, що утримуються через статичне посилання, singleton, або Handler/Runnable після onDestroy — класика. LeakCanary — обов'язковий інструмент в debug-збірці. Додається однією залежністю debugImplementation "com.squareup.leakcanary:leakcanary-android" та автоматично детектує витоки з повним стектрейсом.

OutOfMemoryError найчастіше виникає через завантаження зображень. Bitmap в пам'яті займає ширина × висота × 4 байти. Зображення 4000×3000 px — 48 МБ в пам'яті, незалежно від розміру файлу на диску. Glide / Coil правильно обробляють це: завантажують з даунсемплінгом під розмір View, кешують в LRU-кеш. Завантажувати в ImageView через BitmapFactory.decodeFile — шлях до OOM на пристроях з 2 ГБ RAM. Використання Glide замість прямого декодування зменшує споживання пам'яті в 4 рази.

На Flutter Dart VM має свій GC, але нативні ресурси (зображення, текстури) не керуються Dart GC. Image.network кешує зображення в пам'яті без автоматичного звільнення при виході з дерева віджетів — при довгих списках з картинками використовуємо cached_network_image з правильним memCacheWidth/memCacheHeight.

FPS та UI Performance

60 FPS — 16.67 мс на кадр. 120 FPS (ProMotion) — 8.33 мс. Все що займає більше на main thread — джанк.

Типові причини просадок FPS:

На iOS: синхронне декодування зображень в cellForRowAt. Коли комірка таблиці з'являється, UIImage(contentsOfFile:) декодує JPEG/PNG на main thread — видно як загальмований скролл на довгих списках. Рішення: UIImage.preparingForDisplay() (iOS 15+) або ImageIO з kCGImageSourceCreateThumbnailWithTransform в background queue, результат через DispatchQueue.main.async.

На Android: RecyclerView.Adapter.onBindViewHolder з синхронними операціями. Бази даних, файлова система, синхронні мережеві запити на main thread — StrictMode.ThreadPolicy з detectAll().penaltyLog() в debug-збірці покаже всі порушення.

На Flutter: build() метод викликається часто, він має бути дешевим. setState() на верхньому віджеті перезбирає все дерево. const конструктори, RepaintBoundary, розбиття на дрібні віджети з локальним стейтом — основні інструменти. Flutter DevTools → Performance показує janky frames (червоні) з причинами.

Профілювання Compose: Recomposition Highlighter та трасування через Trace.beginSection в @Composable. remember для дорогих обчислень, derivedStateOf для computed values, LazyColumn замість Column + forEach для довгих списків.

Батарея: Wake locks, WorkManager, мережеві запити

Додаток у топі по витраті батареї — користувач бачить це в налаштуваннях і видаляє. Android Battery Historian (з ADB bug report) показує детальний timeline: wake locks, wakeups, network activity, sensor usage.

Основні споживачі енергії:

  • Постійний GPS (розбираємо в maps-geo)
  • Polling мережі кожні N секунд за代push
  • Утримання wake lock довше необхідного
  • Excessive AlarmManager wakeups

WorkManager з Constraints — правильний спосіб планувати фонові задачі: setRequiredNetworkType, setRequiresBatteryNotLow, setRequiresCharging. ОС батчить задачі та виконує в зручний час.

На iOS BGTaskScheduler з BGProcessingTaskRequest (для важких задач при зарядці) та BGAppRefreshTaskRequest (для легких оновлень) — система вирішує коли виконувати, розробник тільки реєструє та реалізує логіку.

Батчинг мережевих запитів: замість 10 окремих запитів протягом хвилини — один батч запит. Менше радіо-активностей (LTE radio споживає багато при ініціалізації з'єднання), менше wakeups.

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

Платформа Інструмент Що показує
iOS Xcode Instruments (Time Profiler) CPU, call stack, гарячі методи
iOS Allocations Живі об'єкти, піки пам'яті
iOS Leaks Retention cycles
iOS MetricKit Виробничі метрики (crash rate, hang rate, launch time)
Android Android Profiler CPU, Memory, Network, Energy
Android Systrace / Perfetto System-level трейси
Android LeakCanary Витоки пам'яті
Android Battery Historian Енергоспоживання
Flutter Flutter DevTools Recomposition, frame rendering, memory
Flutter Dart Observatory Dart VM profiling

MetricKit на iOS — особливо цінний: реальні дані з пристроїв користувачів, а не симулятора. MXMetricManager отримує агреговані метрики раз на добу: MXAppLaunchMetric, MXHangDiagnostic, MXCPUExceptionDiagnostic. Діагностики по hang та CPU-exceptions містять стектрейс з реального пристрою — золото для діагностики production-проблем.

Типові проблеми та рішення

Проблема Інструмент діагностики Рішення
Cold start > 3 с App Startup / DYLD_PRINT_STATISTICS Лінива ініціалізація, статичне лінкування
OOM на зображеннях LeakCanary / Allocations Glide/Coil з даунсемплінгом
FPS просадки при скролі Graphics (Xcode) / Profile GPU (Android) Фонове декодування, preparingForDisplay
Висока витрата батареї Battery Historian / Energy Log WorkManager, батч запитів, push замість polling

Процес оптимізації

Починаємо з вимірювання, не з припущень. Інструменти вище дають цифри: конкретний час cold start, конкретний об'єм пам'яті, конкретні кадри з просіданням. Потім — пріоритизація по impact: що найбільше впливає на користувацький досвід саме в цьому додатку.

Аудит продуктивності існуючого додатку: 3–5 робочих днів. Реалізація оптимізацій — від тижня до двох місяців залежно від запущеності проблем та архітектури коду.

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

  • Детальний аудит продуктивності з профілюванням на реальних пристроях
  • Звіт з виявленими проблемами та рекомендаціями (PDF/Notion)
  • Код-рев'ю вузьких місць з пропозиціями змін
  • Реалізація оптимізацій (cold start, пам'ять, UI, батарея)
  • Повторне тестування для підтвердження покращень
  • Документація змін та рекомендації щодо подальшої підтримки

Ми оптимізували 50+ мобільних застосунків, серед яких фінансові та соціальні мережі з аудиторією понад 10 млн користувачів. Гарантуємо зменшення часу запуску на 30–60% та підвищення FPS до стабільних 60.

Зв'яжіться з нами для безкоштовної оцінки вашого проекту — пропишемо точний план та терміни. Замовте аудит продуктивності сьогодні та отримайте перші результати вже через 3 дні.