Разработка системы столкновений (Collision System) мобильной игры
Collision detection в мобильной игре — не тот компонент, который «просто работает». Стандартные коллайдеры Unity и Godot хорошо работают на десктопе, но на мобильных устройствах с непостоянным FPS и throttling-ом процессора система столкновений начинает давать сбои именно в самый неподходящий момент.
Главные проблемы штатных коллайдеров на мобиле
Туннелирование — объект на высокой скорости проходит сквозь другой между двумя физическими шагами. CollisionDetectionMode.Continuous в Unity решает это, но стоит примерно в 2 раза дороже по CPU, чем Discrete. На бюджетных Android-устройствах это ощутимо.
Ложные срабатывания на стыках. PolygonCollider2D с несколькими вершинами на стыке двух тайлов часто генерирует «фантомный» коллижн — персонаж спотыкается на ровной поверхности. Это классический баг тайловых игр. В Unity решается через Composite Collider 2D, который объединяет коллайдеры соседних тайлов в одну полигональную форму.
Дорогие MeshCollider. MeshCollider с Convex = false в Unity не участвует в dynamic-to-dynamic коллизиях — только static. Если нужны произвольные формы для динамических объектов, приходится аппроксимировать примитивами: несколько BoxCollider/CapsuleCollider вместо одного MeshCollider. Это ручная работа, но она сокращает нагрузку на broadphase в разы.
Layered collision matrix
Первое, что настраиваем на любом проекте — матрица слоёв (Physics > Layer Collision Matrix). Типичная ошибка новичков — оставить все слои взаимодействующими между собой. В игре с 5 типами объектов это 25 пар проверок вместо 6-8 реально нужных.
Для мобильного проекта это напрямую влияет на broadphase (первая фаза обнаружения коллизий, где Unity отсеивает неподходящие пары по AABB). Меньше активных пар — меньше работы на каждый физический шаг.
Кастомный collision detection для специфических механик
Для некоторых жанров движковая физика избыточна. Пример: раннер, где нужна только коллизия персонажа с землёй и препятствиями. Вместо Rigidbody + Collider — raycast-based система:
void CheckGround() {
RaycastHit2D hit = Physics2D.Raycast(
transform.position,
Vector2.down,
groundCheckDistance,
groundLayer
);
isGrounded = hit.collider != null;
if (isGrounded) groundNormal = hit.normal;
}
void CheckObstacles() {
// BoxCast вперёд по направлению движения
RaycastHit2D hit = Physics2D.BoxCast(
transform.position,
colliderSize,
0f,
Vector2.right,
obstacleCheckDistance,
obstacleLayer
);
if (hit.collider != null) OnObstacleHit(hit);
}
Это легче, полностью детерминировано и даёт прямой контроль над поведением. Нет случайных джитер-эффектов от solver-итераций.
Trigger vs Collision: когда что использовать
OnTriggerEnter / OnCollisionEnter — фундаментальное разграничение. Коллизия — физическое столкновение с импульсом, триггер — логическое пересечение без физики. Частая ошибка: использовать Collision там, где нужен только Trigger, добавляя лишние Rigidbody и нагружая solver.
На Godot 4 аналогия: Area2D для триггеров и зон, CharacterBody2D.move_and_collide() / move_and_slide() для физических взаимодействий. move_and_slide() автоматически скользит по наклонным поверхностям — то, что в Unity нужно реализовывать вручную через нормаль поверхности.
Оптимизация на реальных устройствах
Профилирование в Unity Profiler (Deep Profile) на целевом устройстве обязательно. Смотрим на Physics.Processing и Physics2D.Processing в timeline. Типичные виновники просадок:
- слишком мелкий
Fixed Timestep(делает больше шагов на медленных кадрах) -
Rigidbody.interpolation = Interpolateна десятках объектов - динамические
CompositeCollider2Dс частыми перестройками геометрии
Сроки разработки collision system: простые механики — 3-5 дней, сложные (многослойная геометрия, кастомный детектор, оптимизация под слабые устройства) — 1-3 недели. Стоимость рассчитывается индивидуально.







