Інтеграція фізичного двигуна та налаштування колайдерів

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

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

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

Відвідати персоналізований сайт
Показано 1 з 1Усі 242 послуг
Інтеграція фізичного двигуна та налаштування колайдерів
Середній
від 2 днів до 2 тижнів
Часті запитання

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

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

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

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

Некорректно налаштовані колайдери — одна з найчастіших і малопомітних проблем у Unity-проєктах. Ми, як інженери з 10+ річним досвідом, бачимо це постійно: персонаж провалюється крізь підлогу на високій швидкості, кулі зникають, не влучивши в ціль, фізичні об'єкти тремтять на статичній поверхні. Причини майже завжди в основах: неправильний тип колайдера, відсутність Continuous collision detection, невірне налаштування шарів. Наша команда має 10+ років досвіду та понад 50 завершених проєктів — гарантуємо усунення цих проблем під ключ. Вартість базового налаштування фізики починається від $300.

Чому колайдери налаштовані неправильно?

Unity пропонує шість примітивних колайдерів: BoxCollider, SphereCollider, CapsuleCollider, MeshCollider, WheelCollider, TerrainCollider. Плюс 2D-аналоги. MeshCollider — головне джерело проблем у руках недосвідчених розробників. Convex MeshCollider працює з Rigidbody коректно, але обмежений 255 полігонами. Non-convex не може застосовуватися до динамічних об'єктів взагалі — Unity видасть попередження, але не заблокує, і поведінка буде невизначеною. Для персонажів і снарядів завжди використовуємо примітиви; MeshCollider — тільки для статичної геометрії рівня.

CapsuleCollider оптимальний для персонажів (вертикально) і куль (горизонтально). Два параметри: radius і height. Типова помилка — капсула персонажа налаштована по VisualMesh, а не по геймплейним потребам: занадто широка капсула робить персонажа «жирнішим», ніж він виглядає, і він не пролазить у вузькі проходи. SphereCollider — найдешевший за обчисленнями. Для снарядів, гранат і невеликих предметів переважніший CapsuleCollider, якщо форма дозволяє.

Як налаштувати Layer Matrix?

Physics Layer Collision Matrix (Edit → Project Settings → Physics → Layer Collision Matrix) визначає, які шари взаємодіють. Некорректно вибудована матриця призводить до: снарядів, що влучають у своїх; trigger-зон, що реагують на оточення, а не тільки на гравця; ворогів, що штовхають один одного при скупченні. Правильна структура шарів для типової гри: Default, Player, Enemy, Projectile, Environment, Trigger, Debris. Projectile взаємодіє з Player, Enemy, Environment — але не з Trigger, Debris, іншими Projectile. Trigger не взаємодіє ні з чим фізично — тільки OnTriggerEnter. При використанні Physics.Raycast або Physics.OverlapSphere завжди передавайте LayerMask явно — без маски каст перевіряє всі шари, включаючи невидимі UI-колайдери та зони-тригери, повертаючи несподівані хіти. За документацією Unity, Layer Matrix може знизити навантаження на CPU до 30% при правильному налаштуванні.

Collision Detection Mode: як перемогти тунельний ефект

За замовчуванням Rigidbody використовує Discrete collision detection: позиція об'єкта перевіряється на початку та в кінці кожного FixedUpdate. Якщо об'єкт рухається швидше, ніж size_object / fixedDeltaTime одиниць за секунду, він може «перестрибнути» крізь тонку геометрію — це тунельний ефект. Куля діаметром 0.1 units при швидкості 50 m/s пролітає 50 * 0.02 = 1 unit за один FixedUpdate. Стіна товщиною 0.5 units буде пропущена. Рішення:

  1. Встановіть Rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous для динамічних об'єктів, які можуть тунелювати крізь статику.
  2. Використовуйте CollisionDetectionMode.ContinuousDynamic для об'єктів, які можуть тунелювати крізь інші динамічні об'єкти.
  3. Для куль з дуже високою швидкістю — застосовуйте Physics.Raycast або Physics.SphereCast замість фізичного Rigidbody: це надійніше та продуктивніше.

Continuous режим дорожчий по CPU приблизно в 2 рази порівняно з Discrete, але забезпечує точність зіткнень на 99%. Застосовуємо тільки до швидких снарядів і персонажів — не до всіх об'єктів підряд.

PhysicsMaterial і налаштування тертя

PhysicMaterial задає dynamicFriction, staticFriction і bounciness. Комбінування двох матеріалів контактуючих об'єктів відбувається за правилом frictionCombine і bounceCombine (Average, Minimum, Multiply, Maximum). Для персонажа на Rigidbody: PhysicMaterial з dynamicFriction = 0, staticFriction = 0, frictionCombine = Minimum. Без цього персонаж ковзає по стінах, застрягає на ребрах, несподівано гальмує на похилих поверхнях. Для відскакуючих об'єктів: bounciness = 0.6, bounceCombine = Maximum. При bounceCombine = Average м'яч, кинутий на поверхню з bounciness = 0, не відскочить взагалі — навіть якщо у самого м'яча високе значення.

Compound Colliders і оптимізація

Складні форми об'єктів описуємо кількома примітивними колайдерами на дочірніх об'єктах — compound collider. Один Rigidbody на батьківському об'єкті керує всією фізичною одиницею. Це дешевше MeshCollider і точніше одного BoxCollider. Наприклад, compound collider з 3 BoxCollider зменшує кількість полігонів у 4 рази порівняно з MeshCollider. Для транспортних засобів: окремі BoxCollider для корпусу, бамперів, колісних арок. WheelCollider — спеціалізований компонент для реалістичної поведінки підвіски; не бере участі в стандартних OnCollisionEnter подіях.

Що входить у налаштування фізики під ключ

Етап Деталі
Аудит поточної конфігурації Аналіз колайдерів, Layer Matrix, PhysicMaterial і collision detection
Проєктування та реалізація Налаштування типів колайдерів, compound colliders, фізичних матеріалів
Оптимізація продуктивності Налаштування Layer Matrix, зменшення draw calls за рахунок batching, налаштування solver iteration
Тестування Перевірка на тунельний ефект, стабільність фізики на різних FPS
Документація та навчання Опис архітектури, рекомендації для подальшої розробки

Терміни: від 2 днів для базового налаштування персонажа до 8 тижнів для кастомного фізичного вирішувача. Вартість розраховується індивідуально — зв'яжіться з нами для оцінки.

Задача Термін
Базове налаштування колайдерів персонажа + Layer Matrix 1–2 дні
Фізика транспорту (WheelCollider, suspension) 3–7 днів
Система руйнівних об'єктів (fractured meshes + Rigidbody) 1–2 тижні
Кастомний фізичний вирішувач (без PhysX) 4–8 тижнів

Типові помилки та як їх уникнути

Колайдер перекриває Renderer меш — персонаж візуально проходить крізь стіну, хоча фізично впирається в невидимий бар'єр. Слідкуйте за відповідністю габаритів колайдера та меша. OnCollisionEnter не викликається — один з об'єктів kinematic Rigidbody або StaticCollider без Rigidbody. OnCollisionEnter вимагає Rigidbody хоча б на одному з об'єктів. Для статичної геометрії рівня достатньо OnTriggerEnter на trigger-зонах. Фізичні об'єкти тремтять на місці — solver iteration count занадто низький або об'єкт знаходиться під дією конкуруючих сил. Збільшіть Default Solver Iterations з 6 до 10–12 для складних сцен. Частота помилок при правильному налаштуванні падає на 60%.

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

Програмування ігор: геймплей та системна логіка

До нас приходить проєкт з хаотичною архітектурою. Розробник каже: «працює, не чіпай», але на ділі кожен новий рівень потребує окремого виправлення. Типова картина: контролер персонажа на 2000 рядків, де фізика, анімація, UI та звук перемішані в одному MonoBehaviour. Збереження через PlayerPrefs з ключами типу "player_hp_current_value_int". Це не гіпотетика — прямий результат відсутності архітектурного рішення на початку. Ми займаємося геймплейним програмуванням понад 7 років, реалізували понад 15 проєктів — від мобільних гіперказуалок до PC-шутерів. Гарантуємо, що після нашого втручання проєкт перестає бути «чорною скринькою». Якщо впізнали свою ситуацію — замовте безкоштовний аудит коду, оцінимо стан за 2 дні.

Ми беремо такий код, проводимо аудит і реорганізуємо його в модульну систему. Геймплейне програмування — серце гри. Тут закладається відчуття від керування, інтелект противників, чесна фізика та надійна система прогресу. Зроблено погано — жодний арт не врятує.

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

Який контролер персонажа обрати?

Контролер персонажа задає тон усій грі. Перше, з чим стикається гравець — керування. Затримки, слизький рух, «залипання» на перешкодах — все це зчитується миттєво та псує враження раніше, ніж гравець встигає побачити геймплей. Базовий вибір зводиться до двох варіантів: CharacterController або Rigidbody.

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

Rigidbody — фізичний об'єкт. Необхідний, коли персонаж повинен взаємодіяти з фізичними об'єктами: штовхати ящики, реагувати на вибухи, бути підкинутим. Вимагає акуратної роботи через FixedUpdate та обережного вимкнення гравітації або тертя, щоб керування не здавалося «плаваючим».

Для більшості 3D-проєктів ми використовуємо CharacterController з кастомним обробником гравітації — це дає контроль без артефактів фізичного рушія. Для 2D — Rigidbody2D з constraints на обертання та ретельно налаштованим Collision Detection Mode: Continuous. Кожне рішення приймаємо під жанр та цільову платформу.

Фізика та колізії

Rigidbody та колайдери — джерело регулярних проблем, якщо їх не налаштувати правильно з самого початку. Декілька правил, які економлять час:

  • Collision Detection: Continuous для швидких об'єктів (кулі, снаряди) — інакше вони «пролітають» крізь тонку геометрію
  • Складні меш-колайдери замінюємо складовими примітивами (Box + Capsule + Sphere) — це дешевше для фізики на 70-80%
  • Шари (Physics Layers) та матриця колізій в Physics Settings налаштовуються на початку проєкту — потім додати їх без рефакторингу дуже боляче
  • Всі фізичні обчислення — в FixedUpdate, не в Update. Інакше поведінка залежить від FPS

Дотримання цих правил знижує кількість багів з колізіями на 90% вже на етапі прототипу.

Типові помилки при налаштуванні фізики:

  • Частинки або UI-об'єкти з колайдерами — невидимі перешкоди для куль
  • Тригери, що висять на об'єктах без Rigidbody — події не спрацьовують
  • Швидкість кулі > 100 м/с без Continuous Dynamic — наскрізні прольоти
  • Один колайдер на складну сітку замість композитних — падіння FPS на 40-50%

Як архітектура ШІ впливає на ігровий досвід?

Поганий AI видно одразу: противники застрягають у кутах, атакують крізь стіни, передбачувано патрулюють за одним маршрутом. Різниця між «працює» та «працює добре» тут найбільш відчутна. Розглянемо три рівні деталізації.

Кінцеві автомати (State Machines)

Найпоширеніший підхід — ієрархічний кінцевий автомат (HSM). Кожен стан: Idle, Patrol, Chase, Attack, Dead — це клас або метод з входом, оновленням та виходом. Вікіпедія описує кінцевий автомат як математичну модель поведінки з фіксованим набором станів і переходів.

public enum EnemyState { Idle, Patrol, Chase, Attack, Dead }

private void UpdateStateMachine() {
    switch (_currentState) {
        case EnemyState.Patrol:
            UpdatePatrol();
            if (CanSeePlayer()) TransitionTo(EnemyState.Chase);
            break;
        case EnemyState.Chase:
            _navMeshAgent.SetDestination(_player.position);
            if (InAttackRange()) TransitionTo(EnemyState.Attack);
            if (!CanSeePlayer() && _lostSightTimer > 5f) TransitionTo(EnemyState.Patrol);
            break;
        // ...
    }
}

State Machine добре працює для противників з невеликою кількістю станів (5-8). При зростанні складності — вибухове зростання переходів між станами, код стає важко читати та тестувати.

Behaviour Trees

Behaviour Tree — наступний рівень. Дерево поведінки описує логіку агента через ієрархію задач: Sequence, Selector, Decorator, Leaf. Вікіпедія визначає Behaviour Tree як граф, що дозволяє модульно будувати складну поведінку.

Перевага перед State Machine: кожен вузол атомарний і перевикористовуваний. Вузол CheckLineOfSight написаний один раз і використовується в десяти деревах. Додати нову поведінку — означає додати гілку в дерево, не рефакторити існуючу логіку. Для проєктів з понад 5 типами ворогів BT скорочує час на додавання нового типу в 2-3 рази порівняно зі State Machine.

У Unity BT реалізується через асети (NodeCanvas, Behaviour Designer) або кастомну реалізацію. Для великих проєктів з кількома типами ворогів це окупається вже на етапі другого типу противника. Приклад структури дерева для патрульного противника:

Root
└── Selector
    ├── Sequence (Combat)
    │   ├── IsPlayerVisible
    │   ├── IsPlayerInRange
    │   └── AttackPlayer
    ├── Sequence (Alert)
    │   ├── HeardSound
    │   └── InvestigatePosition
    └── Sequence (Patrol)
        ├── HasPatrolRoute
        └── FollowPatrolRoute

GOAP — коли BT недостатньо

Goal-Oriented Action Planning — підхід для дійсно складного AI, де агент повинен планувати послідовність дій для досягнення мети з урахуванням поточного стану світу. Класичний приклад — противник, якому потрібно «вбити гравця». Якщо в нього немає зброї, він шукає зброю. Якщо немає боєприпасів, він шукає патрони. Якщо гравець сховався, він шукає обхідний маршрут. GOAP дозволяє задати ці дії та їх передумови/постумови, а планувальник будує ланцюжок автоматично.

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

NavMeshAgent та навігація

NavMeshAgent — стандартний інструмент для навігації в Unity. Працює коректно при правильному налаштуванні NavMesh та агентів:

  • Agent Radius та Agent Height повинні точно відповідати колайдеру персонажа
  • Stopping Distance потрібно налаштовувати під дальність атаки кожного типу ворога
  • NavMesh Obstacle з Carve: true для динамічних перешкод (ящики, що падають, двері, що зачиняються) — інакше агенти будуть намагатися пройти крізь них
  • Для великих відкритих світів — NavMesh Links для з'єднання окремих сегментів та Off-Mesh Links для стрибків і спусків
Критерій State Machine Behaviour Tree
Перевикористання логіки Низьке (стани прив'язані до контексту) Високе (вузли незалежні)
Масштабування Вибух переходів при 10+ станів Лінійне зростання дерева
Налагодження Складне (потрібен повний стейт-трекер) Просте (видно поточний вузол)
Складність впровадження Низька (старт за 1 день) Середня (3-5 днів на налаштування)
Рекомендований обсяг До 6 типів ворогів Від 6 типів ворогів

Чому система збережень потребує версіонування?

Друга область, де архітектурні рішення на початку критично впливають на все подальше. Збереження, додані в кінці розробки «за тиждень», майже завжди ламаються при зміні структури даних. Розглянемо інструменти.

PlayerPrefs — коли підходить і коли ні

PlayerPrefs — це сховище простих ключ-значення (string, int, float). Підходить суворо для налаштувань (гучність, керування, мова). Використовувати його для зберігання стану ігрового світу — помилка: немає типізації, немає версіонування, немає зручного дебагу.

JSON-серіалізація

Робочий підхід для більшості проєктів — серіалізація даних у JSON через JsonUtility (вбудований, швидкий, але обмежений) або Newtonsoft.Json (повноцінний, підтримує словники, успадкування, nullable типи). Структура системи збережень:

[Serializable]
public class SaveData {
    public int version = 1;          // версіонування
    public PlayerSaveData player;
    public WorldSaveData world;
    public SettingsSaveData settings;
}

public class SaveSystem : MonoBehaviour {
    private const string SAVE_FILE = "/save.json";

    public void Save(SaveData data) {
        string json = JsonConvert.SerializeObject(data, Formatting.Indented);
        File.WriteAllText(Application.persistentDataPath + SAVE_FILE, json);
    }

    public SaveData Load() {
        string path = Application.persistentDataPath + SAVE_FILE;
        if (!File.Exists(path)) return new SaveData();
        string json = File.ReadAllText(path);
        return JsonConvert.DeserializeObject<SaveData>(json);
    }
}

ScriptableObject як контейнер даних

ScriptableObject — недооцінений інструмент для зберігання ігрових даних. Конфігурації предметів, характеристики противників, параметри рівнів — все це зручніше тримати в ScriptableObject, ніж у JSON або константах у коді. Для збережень ScriptableObject використовується в патерні Runtime Set і Variable: значення зберігаються в ScriptableObject, збереження записує лише дельту відносно дефолтних значень.

Версіонування збережень

Поле version в корені SaveData — не бюрократія, а необхідність. Коли після релізу додається нова механіка з новими полями, потрібно коректно мігрувати старі збереження. Міграційний метод:

private SaveData MigrateSaveData(SaveData data) {
    if (data.version < 2) {
        data.player.newField = defaultValue;
        data.version = 2;
    }
    if (data.version < 3) {
        // наступна міграція
        data.version = 3;
    }
    return data;
}

Без версіонування доводиться вибирати між зламаними збереженнями у гравців або відмовою від зміни структури даних.

Що дає ScriptableObject-архітектура?

Для середніх і великих проєктів ми використовуємо підхід, популяризований Ryan Hipple на GDC: ScriptableObject як шина подій та сховище спільного стану.

// Змінна-подія
[CreateAssetMenu]
public class GameEvent : ScriptableObject {
    private List<GameEventListener> _listeners = new();

    public void Raise() {
        for (int i = _listeners.Count - 1; i >= 0; i--)
            _listeners[i].OnEventRaised();
    }
}

Це дозволяє системам у грі взаємодіяти без прямих посилань одна на одну. PlayerHealth не знає про UI, UI не знає про GameManager — всі вони знають тільки про ScriptableObject-події. Проєкт стає значно легше тестувати та розширювати.

Як ми працюємо: етапи та результат

Кожен проєкт проходить через п'ять етапів. Нижче — орієнтовні терміни та ключові артефакти. Вартість кожного етапу розраховується індивідуально.

Етап Тривалість (робочі дні) Результат
Аудит поточної архітектури 1-3 Документ з проблемами та рекомендаціями
Проектування систем 2-5 Архітектурна схема, опис модулів
Реалізація (ітерації) від 10 Робочий код, протестований у зв'язці з артом
Код-рев'ю та рефакторинг 2-4 Чиста кодова база, коментарі до складних ділянок
Документація та здача 1-2 Гайд для команди, опис налаштувань (Physics Layers, NavMesh тощо)

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

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

Після завершення ви отримуєте:

  • Архітектурну документацію ігрових систем (контролер, AI, збереження)
  • Вихідний код з коментарями, готовий до подальшої розробки
  • Налаштування інструментів: Physics Layers, NavMesh, проєкт ScriptableObject-подій
  • Код-рев'ю існуючих модулів (якщо проєкт не з нуля)
  • Гарантію: безкоштовна підтримка на етапі інтеграції протягом 2 тижнів після здачі

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