Розробка скриптів для процедурної генерації елементів у VR іграх
Процедурна генерація у VR—це не просто випадкове розміщення об'єктів. Користувач знаходиться всередину сцени та фізично поворачує голову: артефакти стиковки тайлів, видимі паттерни повторюваності та «плаваючі» коллайдери ломять іммерсію моментально. Вимоги до якості процедурного контенту у VR на порядок вищі, ніж у плоских іграх.
Головні технічні складності процедурної генерації у VR
Перша та найболісніша проблема—генерація геометрії в главному потоці. Mesh.SetVertices() + Mesh.RecalculateNormals() на 50 000 вершин в OnEnable займає 12–18 мс на Snapdragon XR2, що дає прямий frame drop нижче 72 FPS—поріг комфортного VR. Розв'язується через Job System з IJobParallelFor та передачею даних через NativeArray<Vector3>, з подальшим застосуванням через Mesh.ApplyAndDisposeWritableMeshData() вже в главному потоці.
Друга проблема—коліізії для процедурної геометрії. MeshCollider з convex: false на динамічно генерованому меші на Quest заборонений PhysX з міркувань продуктивності (і Oculus VRC його не пропустить). Або розбиваємо геометрію на опуклі примітиви, або генеруємо спрощений collision mesh окремо через алгоритм Quickhull. У складних сценах—замінюємо MeshCollider на масив BoxCollider/SphereCollider за вузлами генерованої структури.
Третя—детерміністичність. Процедурна сцена повинна відтворюватися однаково при тому ж seed. Проблема виникає, коли генератор змішує Random.value (який залежить від глобального стану) та System.Random з фіксованим seed. Тримаємо весь генератор на одному екземплярі System.Random(seed) без звертання до UnityEngine.Random всередину pipline.
Як ми будуємо процедурні генератори для VR
Архітектура типового генератора для VR-рівня будується на трьох шарах: Layout Generator (розміщення ключових точок, шляхів, зон), Detail Populator (наповнення геометрією, мешами, ассетами з пула) та LOD Manager (керування детализацією залежно від дистанції до HMD).
Для генерації terrain-подібних структур використовуємо Perlin Noise через Mathf.PerlinNoise() з октавами—стандарт, але у VR діапазон висот критичний: перепади більше 30° нахилу поверхні викликають дискомфорт при ходьбі з фізичним контролером. Інтегруємо slope-check прямо в генератор, обрізаючи екстремальні значення.
Для міських та інтер'єрних сцен працює BSP (Binary Space Partitioning) або Wave Function Collapse—WFC особливо хороший для тайлових рівнів з чіткими правилами стиковки. Реалізуємо WFC з backtracking, обмеженим за depth, щоб не піти в нескінченний перебір при складних constraints.
З реального кейса: у проекті архітектурної VR-візуалізації генератор планування квартир на базі WFC спочатку давав 40% випадків з «невалідними» планами (кімната без виходу, перекривающиеся стіни). Виправили додаванням pre-pass валідатора з правилами зв'язності через BFS за графом кімнат—перед фінальною матеріалізацією меша.
Пул об'єктів критичний. Інстанцирування 500 GameObject через Instantiate() при змінові рівня—200–400 мс фриз. Використовуємо ObjectPool<T> з UnityEngine.Pool (доступний з Unity 2021), преднаповнений у фоновому потоці через AsyncInstantiateOperation.
Етапи роботи
Аналіз контенту. Вивчаємо, що саме потрібно генерувати: геометрія, розстановка об'єктів, нарратив, навігація. Від цього залежить вибір алгоритму.
Прототип генератора. Швидка реалізація з візуалізацією в Editor-режимі через Gizmos—клієнт видит результат без збірки під HMD.
Оптимізація під VR. Перенесення обчислень у Job System, реалізація пула, налаштування LOD для генерованої геометрії.
Інтеграція з рівнем. Підключення до NavMesh (baking після генерації через NavMeshSurface.BuildNavMesh()), налаштування Occlusion Culling для процедурних об'єктів.
Тестування. Стресс-тест: 100 генерацій з різними seed, перевірка на артефакти, валідація коліцій, замір часу генерації на цільовому HMD.
| Тип генератора | Строки розробки |
|---|---|
| Розстановка об'єктів за правилами (без геометрії) | 3–7 днів |
| Тайловий рівень з WFC | 2–3 тижні |
| Процедурна геометрія з Job System | 3–5 тижнів |
| Повний генератор рівня з NavMesh та LOD | 1–3 місяців |
Вартість визначається після розбору технічного завдання та оцінки складності алгоритміки.





