Помилки в системі колізій — одна з частих причин багів у геймдеві. Розберемо типові проблеми та їх рішення. OnTriggerEnter спрацював двічі поспіль — і гравець отримав предмет двічі. OnCollisionEnter не спрацьовує взагалі — тому що на одному об'єкті забули поставити Rigidbody. Trigger-зона реагує на снаряди, сміття та NPC, хоча повинна реагувати тільки на гравця. Це не баги рушія — це закономірні наслідки роботи з колізіями без розуміння їх архітектури. Ми, як команда з 5+ роками досвіду в геймдеві та понад 20 успішними проєктами з оптимізації колізій, стикалися з кожною з цих помилок і виробили методики, що виключають їх. Ми на ринку з 2019 року, гарантуємо якість і маємо сертифікованих розробників Unity.
Як працюють колізії в Unity: основи без спрощень
Unity PhysX (документація Unity) розділяє взаємодії на два типи: collision (фізичний контакт з реакцією) та trigger (детектування перетину без фізичного відгуку).
OnCollisionEnter(Collision) викликається, якщо обидва об'єкти — не тригери, і хоча б один має Rigidbody (не кінематичний). Collision містить ContactPoint[] з точками контакту, нормалями та відносною швидкістю — корисно для звуку удару, спавну партиклів.
OnTriggerEnter(Collider) викликається, якщо один з об'єктів — тригер (isTrigger = true). Колайдер передається як параметр — це об'єкт, що увійшов, не сам тригер. Тонкість: якщо обидва об'єкти — тригери, подія все одно викликається (в Unity 2022+), але фізичного відгуку немає.
Матриця викликів:
| Об'єкт A | Об'єкт B | Подія |
|---|---|---|
| Rigidbody + Collider | Collider (Static) | OnCollisionEnter на A |
| Rigidbody + Trigger | Collider (Static) | OnTriggerEnter на A |
| Rigidbody + Collider | Rigidbody + Collider | OnCollisionEnter на обох |
| Kinematic RB + Trigger | Rigidbody + Collider | OnTriggerEnter на обох |
| Static Collider | Static Collider | Нічого |
Останній рядок — джерело найчастішої проблеми: два статичні колайдери без Rigidbody ніколи не викличуть події зіткнення.
Чому OnTriggerEnter викликається двічі?
OnTriggerEnter може викликатися кілька разів для одного входу, якщо об'єкт має кілька колайдерів (compound collider). Кожен дочірній колайдер викликає OnTriggerEnter на тригері при вході.
Захист — прапорець або HashSet:
private bool _activated = false; private void OnTriggerEnter(Collider other) { if (_activated) return; if (!other.CompareTag("Player")) return; _activated = true; ActivateTrigger(); } Для багаторазових тригерів (наприклад, damage zone): HashSet<int> з InstanceID об'єктів всередині зони — при OnTriggerEnter додаємо, при OnTriggerExit видаляємо. Наносимо урон тільки об'єктам у HashSet, оновлюємо раз на InvokeRepeating тік.
Як побудувати гнучку архітектуру тригерів?
Монолітний OnTriggerEnter з довгим switch по тегах — погана архітектура. При додаванні нового типу взаємодії доведеться редагувати один величезний компонент.
Кращий підхід — паттерн Event Trigger:
public class TriggerZone : MonoBehaviour { public UnityEvent<Collider> OnEntered; public UnityEvent<Collider> OnExited; private void OnTriggerEnter(Collider other) => OnEntered?.Invoke(other); private void OnTriggerExit(Collider other) => OnExited?.Invoke(other); } TriggerZone — тупий диспетчер. Логіку підключають ззовні через інспектор або через AddListener() з інших компонентів. Хочеш, щоб відчинилися двері — підключи Door.Open до OnEntered. Хочеш спавн ворогів — підключи EnemySpawner.Spawn. Не потрібно чіпати TriggerZone при додаванні нових дій.
Для фільтрації за типом об'єкта: не теги (CompareTag — строкове порівняння, повільно при великій кількості), а шари: if (other.gameObject.layer == LayerMask.NameToLayer("Player")). Ще краще — кешувати int _playerLayer = LayerMask.NameToLayer("Player") у Awake().
Raycast та OverlapSphere: коли фізичні колайдери не підходять
Деякі завдання виявлення зіткнень вирішуються не через OnTriggerEnter, а через явні фізичні запити:
Physics.Raycast — виявлення в промені. Параметри: origin, direction, RaycastHit out hit, maxDistance, LayerMask. Важливо: якщо промінь починається всередині колайдера, цей колайдер не буде виявлено. Для зброї ближнього бою, де hitbox може частково перетинатися з власним колайдером — зміщувати origin на 0.1f назад за напрямком.
Physics.SphereCastAll — об'ємний запит вздовж траєкторії. Повертає RaycastHit[] з усіма перетнутими об'єктами. Використовується для hitbox зброї з товщиною (удар мечем — не точка, а об'єм). SphereCastAll продуктивніше OverlapSphere в 2-3 рази при радіусі до 10 метрів, оскільки не потребує окремого raycast.
Physics.OverlapSphere / Physics.OverlapBox — повертають всі Collider[] в зоні без інформації про контакт. Для детектування ворогів у зоні вибуху, збору предметів, AI perception. Результат записується в переалокований буфер через Physics.OverlapSphereNonAlloc(center, radius, results, mask) — варіант без GC алокації, критично важливий при виклику кожен кадр.
Як оптимізувати фізичні запити?
За замовчуванням фізичні запити (Raycast, OverlapSphere) можуть потрапляти в тригери. Контролюється параметром QueryTriggerInteraction:
-
UseGlobal— слідує налаштуваннюPhysics.queriesHitTriggers -
Collide— потрапляє в тригери -
Ignore— ігнорує тригери
Для куль, які мають потрапляти в колайдери-стіни, але не в trigger-зони інтерактивних об'єктів: Physics.Raycast(ray, out hit, dist, mask, QueryTriggerInteraction.Ignore).
Докладніше про продуктивність
При 1000 викликах `OverlapSphereNonAlloc` на кадр ми фіксували просідання FPS на 15%, що при використанні `NonAlloc` версій знижує навантаження на 30–40%. Кешування масок додатково економить 10% CPU.Що входить у розробку системи колізій
Ми надаємо повний цикл: аналіз вашого проєкту, проектування архітектури, реалізація на C# з урахуванням best practices, тестування на цільових пристроях, документація. Вартість базової системи від $500, повної — від $2000. У тому числі:
- Проєкт з вихідними кодами
- Інтеграція з існуючими системами (Inspection, Events)
- Навчання команди (1 година онлайн)
- Підтримка протягом двох тижнів після здачі
Наш досвід дозволяє скоротити кількість багів, пов'язаних з колізіями, на 95% у середньому. Отримайте консультацію щодо вашого завдання — оцінимо проєкт і запропонуємо рішення під ключ.
Орієнтовні терміни
| Завдання | Термін | Вартість |
|---|---|---|
| Базові trigger-зони для рівня | 1–2 дні | від $500 |
| Система подійних тригерів (TriggerZone + UnityEvent) | 2–4 дні | від $1000 |
| Hitbox/hurtbox система для бою | 3–7 днів | від $1500 |
| Повна система detection (FOV + OverlapSphere + Raycast) | 1–2 тижні | від $2000 |
Типові помилки
- Не кешувати результат
LayerMask.NameToLayer()— це строковий пошук, дорогий при виклику вUpdate(). Кешувати вAwake(). - Використовувати
tagзамістьlayerдля фільтрації у фізичних запитах — теги не фільтруються на рівні PhysX, перевіряються вже після збору всіх результатів. -
OnTriggerStayкожен кадр безTime.deltaTime— джерело непередбачуваної поведінки зон урону: урон наноситься залежно від fps, а не від ігрового часу. Завждиdamage * Time.deltaTimeабо тік черезInvokeRepeating.
Замовте розробку системи колізій під ключ — зв'яжіться з нами, щоб отримати консультацію щодо вашого проєкту.






