Розробка систем виявлення зіткнень та тригерів

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

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

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

Відвідати персоналізований сайт
Показано 1 з 1 послугУсі 242 послуг
Розробка систем виявлення зіткнень та тригерів
Середня
від 2 робочих днів до 2 тижнів
Часті питання

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

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

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

  • image_games_mortal_motors_495_0.webp
    Розробка гри для компанії Mortal Motors
    683
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Покрокова стратегія у фентезі сеттингу With Fire And Sword
    862
  • image_games_second_team_604_0.webp
    Розробка ігри для компанії Second term
    491
  • image_games_phoenix_ii_606_0.webp
    3D-анімація – тизер для гри phoenix 2.
    533

Розробка систем обнаруження столкновень та триггерів

OnTriggerEnter сработав двічі підряд — і гравець отримав предмет два рази. OnCollisionEnter не срабатувает взагалі — тому що на одному об'єкті забули поставити Rigidbody. Trigger-зона реагує на снаряди, мусор та NPC, хоча повинна реагувати тільки на гравця. Це не баги рушія — це закономірні наслідки роботи з коллізіями без розуміння їхної архітектури.

Як працюють коллізії у Unity: основи без упрощень

Unity PhysX розділяє взаємодії на два типи: 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 може викликатися кілька разів для одного входу, якщо об'єкт має кілька коллайдерів (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 тик.

Архітектура trigger-системи для рівнів

Монолітний 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 зброї з товщиною (удар мечем — не точка, а об'єм). Продуктивніше за OverlapSphere в кінці шляху + raycast на початку.

Physics.OverlapSphere / Physics.OverlapBox — повертають усі Collider[] у зоні без інформації про контакт. Для вворогів у зоні вибуху, збору предметів, AI perception. Результат записується у перераспределяємий буфер через Physics.OverlapSphereNonAlloc(center, radius, results, mask) — варіант без GC алокації, критичний при вимозі на кадр.

Оптимізація: QueryTriggerInteraction

За замовчуванням фізичні запити (Raycast, OverlapSphere) можуть попадати у триггери. Контролюється параметром QueryTriggerInteraction:

  • UseGlobal — слідує настройці Physics.queriesHitTriggers
  • Collide — попадає у триггери
  • Ignore — ігнорує триггери

Для куль, які повинні попадати у коллайдери-стіни, але не у trigger-зони інтерактивних об'єктів: Physics.Raycast(ray, out hit, dist, mask, QueryTriggerInteraction.Ignore).

Орієнтовні строки

Задача Строк
Базові trigger-зони для рівня 1–2 дня
Система событійних триггерів (TriggerZone + UnityEvent) 2–4 дня
Hitbox/hurtbox система для боювання 3–7 днів
Повна система detection (FOV + OverlapSphere + Raycast) 1–2 тижні

Типічні помилки

Не кешувати результат LayerMask.NameToLayer() — це строковий пошук, дорогий при вимозі в Update(). Кешувати в Awake().

Використовувати tag замість layer для фільтрації у фізичних запитах — теги не фільтруються на рівні PhysX, перевіряються уже після збору всіх результатів.

OnTriggerStay кожен кадр без Time.deltaTime — джерело непередбачуваної поведінки зон урону: урон наносится в залежності від fps, а не від ігрового часу. Завжди damage * Time.deltaTime або тик через InvokeRepeating.