Столкновения и триггеры: OnCollision и OnTrigger
Столкновения бывают двух видов: «твёрдые» (герой упирается в стену) и «проходные» (герой собирает монету сквозь неё). Разница — в галочке IsTrigger.
Суть: обычный коллайдер даёт физическое столкновение — объекты не проходят друг сквозь друга, событие ловит
OnCollisionEnter2D. Если включитьIsTrigger, коллайдер становится триггером — объекты проходят насквозь, но событиеOnTriggerEnter2Dвсё равно срабатывает. Триггеры — для зон, монет, чек-поинтов.
Подумай о двух ситуациях. Герой бежит и упирается в стену — это столкновение: его должно остановить. Герой пробегает сквозь монету и подбирает её — это триггер: монета не должна его тормозить, но факт касания важен. Обе ситуации — про коллайдеры, разница в галочке Is Trigger.
Как работает под капотом
Is Trigger выключен Is Trigger включён
(СТОЛКНОВЕНИЕ) (ТРИГГЕР)
объекты: отталкиваются проходят насквозь
событие: OnCollisionEnter2D OnTriggerEnter2D
пример: герой и стена герой и монета / зона уронаUnity вызывает три фазы события: ...Enter2D (момент касания), ...Stay2D (пока касаются), ...Exit2D (момент расставания). Чаще всего нужен Enter.
using UnityEngine;
public class PlayerPickup : MonoBehaviour
{
private int coins = 0;
// Триггер: монета помечена Is Trigger
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Coin"))
{
coins++;
Destroy(other.gameObject); // убрать монету
Debug.Log("Монет: " + coins);
}
}
// Столкновение: с врагом
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.CompareTag("Enemy"))
Debug.Log("Получили урон!");
}
}Смоделируем обработку событий касания на Python — диспетчер по типу события и тегу:
# События приходят как (тип, тег). Реагируем по-разному
state = {"hp": 100, "coins": 0}
def handle(event_type, tag):
if event_type == "trigger" and tag == "Coin":
state["coins"] += 1
return "Монета подобрана (прошли насквозь)"
if event_type == "collision" and tag == "Enemy":
state["hp"] -= 25
return "Удар о врага (оттолкнулись)"
if event_type == "trigger" and tag == "DamageZone":
state["hp"] -= 10
return "Вошли в зону урона"
return "Ничего"
events = [("trigger", "Coin"), ("collision", "Enemy"), ("trigger", "DamageZone")]
for et, tag in events:
print(handle(et, tag), "| hp:", state["hp"], "монеты:", state["coins"])Та же логика на Python ▶ — движок просто доставляет тебе событие «коснулись, вот кто», а решение (урон, подбор, ничего) принимает твой код по типу события и тегу. Это сердце игрового взаимодействия.
Частые ошибки
- Ждать OnTrigger без галочки Is Trigger. Если коллайдер не помечен триггером, вызовется OnCollision, а не OnTrigger. И наоборот.
- Ни одного Rigidbody. Чтобы событие сработало, хотя бы у одного объекта нужен Rigidbody2D (часто Dynamic или Kinematic).
- Тяжёлый код в OnCollisionStay2D. Stay вызывается каждый кадр контакта — не клади туда дорогую логику.
- Destroy не того объекта. В триггере монеты уничтожай
other.gameObject(монету), а не себя.
Best practices
- Зоны, бонусы, чек-поинты, концы уровня — делай триггерами; твёрдую геометрию — обычными коллайдерами.
- Внутри обработчика сразу проверяй тег через
CompareTag— не реагируй на всё подряд. - Для частых событий (зона урона) используй таймер, а не урон каждый кадр в Stay.
Итоги: обычный коллайдер — физическое столкновение (OnCollisionEnter2D), коллайдер с Is Trigger — проходное событие (OnTriggerEnter2D). Для срабатывания нужен хотя бы один Rigidbody2D. Движок сообщает о касании, а реакцию определяешь ты по типу события и тегу.
Порядок событий и уничтожение объектов
Один тонкий момент, на котором спотыкаются даже опытные: что происходит, когда в обработчике столкновения ты уничтожаешь объект. Вызов Destroy не убирает объект мгновенно — он лишь помечает его на удаление в конце текущего кадра. Поэтому код после Destroy в том же кадре ещё может обращаться к объекту, но полагаться на это не стоит. Опасная ситуация — когда два объекта одновременно пытаются уничтожить друг друга при столкновении: легко получить двойной подсчёт очков или двойной урон. Защита проста: ставь флаг «уже обработано» и проверяй его в начале обработчика. Ещё одно правило гигиены — внутри обработчика всегда проверяй тег второго участника прежде, чем что-то делать: иначе твоя монета среагирует на стену, врага и стрелу одинаково, и логика поплывёт.