Работая над уровнем в Unity, составленным из блоков или модульных элементов, вы наверняка сталкивались с неприятной ситуацией: шар (или другой объект) катится по поверхности, но внезапно отскакивает в случайном направлении. Эта проблема, известная как «призрачные столкновения» (ghost collisions), может стать настоящей головной болью, особенно в играх с физикой, таких как мини-гольф или платформеры. В этой статье мы разберем, почему возникают призрачные столкновения, и рассмотрим два решения: быстрое, но не совсем надежное, и более сложное, но масштабируемое.
Почему возникают призрачные столкновения?
Физический движок Unity работает на основе дискретных «снимков», которые обновляются с фиксированным временным шагом. Когда объект, например шар, движется по поверхности, состоящей из нескольких коллайдеров, движок может ошибочно интерпретировать швы между ними как углы. Это приводит к тому, что объект «зацепляется» за несуществующий угол и отскакивает. Даже использование непрерывного обнаружения столкновений (Continuous или Continuous Dynamic) не всегда решает проблему, так как физика Unity чувствительна к микроскопическим зазорам между коллайдерами.
Эта проблема особенно заметна в играх, где уровень состоит из множества блоков, как в мини-игре в гольф на примере ниже. Несмотря на то, что поверхность казалась плоской, шар постоянно отскакивал при переходе между блоками.

Решение 1: Быстрый хак с Default Contact Offset
Самый простой способ устранить призрачные столкновения — изменить параметр Default Contact Offset в настройках физики Unity (Edit → Project Settings → Physics). По умолчанию он равен 0.01, но если уменьшить его, например, до 0.001, вероятность ложных столкновений значительно снижается.
Как это сделать:
Откройте настройки физики в Project Settings.
Найдите параметр Default Contact Offset.
Измените значение с 0.01 на 0.001.
Сохраните и протестируйте сцену.
Плюсы:
Не требует написания кода.
Быстро реализуется и решает проблему в большинстве случаев.
Минусы:
Unity предостерегает от слишком низких значений, так как это может вызвать дрожание (jitter) объектов, особенно при высоких скоростях.
Это не масштабируемое решение и может привести к другим проблемам с физикой в проекте.
Решение 2: Управление контактами через OnContactModifyEvent
Для более надежного и гибкого решения можно использовать API Unity для модификации контактов — событие OnContactModifyEventCCD (или OnContactModifyEvent для объектов без непрерывного обнаружения столкновений). Этот метод позволяет фильтровать нежелательные контакты и сглаживать швы между коллайдерами, предотвращая призрачные столкновения.
Как это работает:
Создайте скрипт, который будет обрабатывать событие OnContactModifyEventCCD.
В этом событии вы получаете массив пар контактов (ModifiableContactPair), которые можно анализировать и изменять.
Проверяйте расстояние между коллайдерами. Если оно больше нуля (что указывает на шов), переназначьте нормаль контакта, например, на Vector3.up, чтобы движок воспринимал поверхность как плоскую.
Пример кода:
using UnityEngine;
using Unity.Collections;
public class Ball : MonoBehaviour
{
private int colliderInstanceID;
public enum BouncePreventionMode { None, Simple }
public BouncePreventionMode bouncePrevention = BouncePreventionMode.Simple;
void Awake()
{
colliderInstanceID = GetComponent<Collider>().GetInstanceID();
Physics.ContactModifyEventCCD += OnContactModifyEvent;
}
void OnContactModifyEvent(PhysicsScene physicsScene, NativeArray<ModifiableContactPair> contactPairs)
{
if (bouncePrevention == BouncePreventionMode.Simple)
{
for (int i = 0; i < contactPairs.Length; i++)
{
var pair = contactPairs[i];
if (pair.colliderInstanceID == colliderInstanceID && pair.separation > 0)
{
pair.normal = Vector3.up; // Сглаживаем шов, устанавливая нормаль вверх
}
}
}
}
}
Плюсы:
Полный контроль над физическими контактами.
Не вызывает дрожания объектов.
Масштабируемое решение, подходящее для продакшена.
Поддерживает использование Job System для оптимизации производительности в сложных сценах.
Минусы:
Требует написания и отладки кода.
Нужно разбираться в API физики Unity.
Этот подход рекомендуется для серьезных проектов, так как он позволяет точно настраивать поведение физики под конкретные нужды игры.
Бонусное решение: Havok Physics
Если вы используете физический движок Havok (альтернатива стандартной физике Unity), есть третье решение — функция Contact Point Welding. Она автоматически устраняет призрачные столкновения для выбранных объектов, выполняя сложные вычисления "под капотом". Это готовое решение, но оно доступно только при использовании Havok, что подходит не всем проектам.
Призрачные столкновения — распространенная проблема при работе с модульными уровнями в Unity, но ее можно решить. Если вам нужно быстрое решение, уменьшение Default Contact Offset может помочь, но лучше использовать API модификации контактов или, если доступно, Havok Physics. Эти подходы позволят вам создать плавный и стабильный геймплей, избежав неожиданных отскоков.
Александр Антипин, студия Metabula Games
Jijiki
вообще тема шва интересная ) поидее если это не значительный шев можно сделать сдвиг в ту сторону в какую смотрим, потомучто швы не только в 3д есть, я в рейкаст движке тут недавно глитч наблюдал встав на шев и прыгнув ) тоесть я встал на шев портала и прыгнул, НО если сделать микрошаг вперед то движок считает что я в портале и соотв уже не прыгнешь) тоже можно пофиксить поидее )