Коллизии: AABB, OBB, круговые и полигональные
Что такое коллизия и зачем она нужна?
Представь, что в игре персонаж подходит к стене — и не проходит сквозь неё 🧱. Пуля попадает во врага — и наносит урон 💥. Всё это работает благодаря коллизиям (столкновениям). Коллизия — это обнаружение пересечения между объектами. Без неё игра просто развалилась бы!
🎮 Коллизии используются не только для физики, но и для определения клика по кнопке, попадания луча и многого другого!
Сегодня мы разберём четыре основных типа коллизий, которые чаще всего используются в геймдеве.
AABB (Axis-Aligned Bounding Box)
Это самый простой и популярный вид коллизии. AABB — это прямоугольник, стороны которого строго параллельны осям X и Y 📦. Его очень легко проверить на пересечение!
Как представить AABB? Просто запомни две точки: левый нижний угол и правый верхний.
min_x = 10
min_y = 10
max_x = 20
max_y = 20
Два AABB пересекаются, если:
- Правый край первого объекта левее левого края второго? → Нет пересечения ❌
- Левый край первого объекта правее правого края второго? → Нет пересечения ❌
- Верхний край первого объекта ниже нижнего края второго? → Нет пересечения ❌
- Нижний край первого объекта выше верхнего края второго? → Нет пересечения ❌
- Во всех остальных случаях — есть пересечение! ✅
В коде это выглядит так:
def check_aabb_collision(box1, box2):
if (box1.max_x < box2.min_x or box1.min_x > box2.max_x or
box1.max_y < box2.min_y or box1.min_y > box2.max_y):
return False
return True
💡 AABB не поворачивается вместе с объектом! Он всегда параллелен осям. Это и плюс (простота), и минус (неточность для повёрнутых объектов).
Задача на AABB
У нас есть два прямоугольника:
- Первый: от (2, 3) до (5, 6)
- Второй: от (4, 5) до (7, 8)
Сталкиваются ли они?
Решение
- Сравниваем по X: max_x первого = 5, min_x второго = 4 → 5 > 4, и min_x первого = 2, max_x второго = 7 → 2 < 7 ✅
- Сравниваем по Y: max_y первого = 6, min_y второго = 5 → 6 > 5, и min_y первого = 3, max_y второго = 8 → 3 < 8 ✅
- Ни одно из условий непересечения не выполнилось → коллизия есть! 🎯
OBB (Oriented Bounding Box)
А что, если наш объект повёрнут? 📐 AABB уже не справится, потому что он будет слишком большим и неточным. На помощь приходит OBB — повёрнутый прямоугольник.
OBB учитывает поворот объекта, поэтому его грани не параллельны осям. Проверка коллизии сложнее, но точнее.
Самый популярный алгоритм для проверки OBB — Separating Axis Theorem (SAT) — теорема о разделяющей оси. Если между двумя объектами можно провести прямую, которая их разделит — коллизии нет.
📐 Для OBB достаточно проверить 4 оси (нормали к сторонам каждого прямоугольника).
Алгоритм SAT (упрощённо):
- Спроецировать оба многоугольника на ось.
- Если проекции не пересекаются хотя бы на одной оси — коллизии нет.
- Если пересекаются на всех осях — коллизия есть.
Реализация OBB сложнее, но она даёт точность для повёрнутых объектов!
Круговые коллизии
С кругами всё гораздо проще! 🎯 Нужно лишь проверить расстояние между центрами.
Формула проверки коллизии двух кругов:
distance = sqrt((x2 - x1)**2 + (y2 - y1)**2)
if distance < (radius1 + radius2):
коллизия есть!
Чтобы избежать дорогого вычисления квадратного корня, часто сравнивают квадраты расстояний:
distance_sq = (x2 - x1)**2 + (y2 - y1)**2
if distance_sq < (radius1 + radius2)**2:
коллизия есть!
Задача на круговые коллизии
Два круга: первый в точке (0, 0) с радиусом 5, второй в точке (8, 0) с радиусом 3. Есть ли коллизия?
Решение
- Вычисляем расстояние между центрами:
8 - 0 = 8 - Сумма радиусов:
5 + 3 = 8 - Расстояние (8) равно сумме радиусов (8) → круги касаются, но не пересекаются! Это крайний случай, который обычно не считают коллизией. ❌
🌀 Круги отлично подходят для шаров, пуль, зон приближения — там, где не нужна высокая точность по форме.
Полигональные коллизии
Самый точный и сложный тип — когда коллайдер повторяет форму объекта 🔺. Используется для сложных форм, например, машин или персонажей.
Полигон (многоугольник) задаётся списком вершин. Проверка коллизии обычно тоже использует SAT, но осей уже больше — по числу нормалей к сторонам каждого полигона.
Основные шаги проверки для двух полигонов:
- Взять первую ось (нормаль к стороне первого полигона).
- Спроецировать оба полигона на эту ось.
- Если проекции не пересекаются — коллизии нет.
- Повторить для всех осей первого полигона.
- Повторить для всех осей второго полигона.
- Если на всех осях проекции пересекаются — коллизия есть.
⚠️ Это computationally expensive (вычислительно дорого)! Поэтому для сложных моделей часто используют комбинацию: сначала проверяют простые коллайдеры (AABB/круги), а если они пересеклись, тогда уже проверяют полигональную коллизию.