~3 мин чтения

Физика — Rigidbody, Collider и столкновения

Как двигать объекты физикой и реагировать на столкновения.

Иллюстрация: Физика — Rigidbody, Collider и столкновения

Unity использует PhysX от NVIDIA для 3D-физики (для 2D — Box2D). Это означает: симуляция твёрдых тел, столкновения, силы, joints. Всё это работает из коробки, ваша задача — корректно настроить компоненты.

Три класса участников

  1. Static — GameObject с Collider, без Rigidbody. Не движется, идеален для стен, пола, статичной геометрии.
  2. Kinematic — Rigidbody с isKinematic = true. Двигается только через ваш скрипт (через MovePosition / MoveRotation), не реагирует на силы.
  3. Dynamic — обычный Rigidbody. Реагирует на гравитацию, силы, столкновения.
Двигать static-коллайдер плохо

Если у объекта нет Rigidbody, Unity кеширует его в так называемом “static collider tree”. Перемещение такого коллайдера каждый кадр заставит PhysX перестраивать дерево — дорого. Нужно двигать что-то без физики? Поставьте Rigidbody с isKinematic = true.

Rigidbody

Основные поля:

  • mass — масса в кг.
  • linearVelocity (раньше velocity) — текущая линейная скорость в м/с. Старое имя velocity помечено [Obsolete] в Unity 6.
  • angularVelocity — угловая скорость в рад/с.
  • linearDamping / angularDamping — новые имена в скриптинге Unity 6 (раньше drag / angularDrag). Старые имена drag/angularDrag пока работают и не помечены [Obsolete]; в Inspector свойства до сих пор отображаются как “Linear Drag” / “Angular Drag”.
  • useGravity — применять ли гравитацию Physics.gravity (по умолчанию (0, -9.81, 0)).
  • isKinematic — управляется ли только скриптами.
  • interpolationNone / Interpolate / Extrapolate. Сглаживает движение между шагами физики. Для игрока обычно ставят Interpolate.
  • collisionDetectionModeDiscrete / Continuous / ContinuousDynamic / ContinuousSpeculative. Для быстрых объектов (пуль, мячей) важно Continuous-варианты, чтобы избежать “проскакивания” сквозь тонкие коллайдеры.
`velocity` → `linearVelocity` в Unity 6

В Unity 6 поле velocity помечено [Obsolete] (компилятор выдаст warning) — используйте linearVelocity в новом коде. Для drag/angularDrag Obsolete-маркер пока не выставлен, но идиоматично в новых проектах — linearDamping/angularDamping. angularVelocity остался без переименования.

public class Ball : MonoBehaviour
{
    private Rigidbody _rb;

    private void Awake() {
        _rb = GetComponent<Rigidbody>();
    }

    private void FixedUpdate() {
        // Импульс — мгновенное изменение скорости
        if (Input.GetKey(KeyCode.Space)) {
            _rb.AddForce(Vector3.up * 10f, ForceMode.Impulse);
        }
    }
}

Режимы силы

AddForce принимает ForceMode:

ModeСмыслФормула
ForceСила, действует постоянноΔv = (F/m) * dt
AccelerationУскорение без учёта массыΔv = a * dt
ImpulseМгновенный импульсΔv = F/m
VelocityChangeМгновенное изменение скоростиΔv = F

Для прыжка обычно Impulse или VelocityChange. Для постоянной тяги (ракетный двигатель) — Force.

Collider — форма для физики

Collider описывает геометрию столкновения. Несколько типов:

  • BoxCollider, SphereCollider, CapsuleCollider — примитивы. Дёшево, точно.
  • MeshCollider — произвольный mesh. Дорого. Должен быть convex, если объект динамический.
  • TerrainCollider — для рельефа.
  • WheelCollider — специализированный, для машин (с подвеской и трением шины).
Используйте примитивы где возможно

Сложная статика? Один MeshCollider достаточно. Динамический объект сложной формы? Соберите его из нескольких BoxCollider/SphereCollider — производительность на порядки выше, чем у одного MeshCollider.

Триггеры vs столкновения

У Collider есть флажок isTrigger. Если он включён, объект не блокирует другие — но генерирует события OnTrigger*. Без isTrigger это физическое столкновение, генерирует OnCollision*.

// Сбор монеток через триггер
private void OnTriggerEnter(Collider other) {
    if (other.CompareTag("Player")) {
        ScoreManager.Instance.Add(1);
        Destroy(gameObject);
    }
}

// Реакция на столкновение
private void OnCollisionEnter(Collision collision) {
    var contact = collision.GetContact(0);
    Debug.Log($"Удар в {contact.point}, сила: {collision.impulse.magnitude:F2}");
    AudioSource.PlayClipAtPoint(_hitSound, contact.point);
}

Правила вызовов событий

Не интуитивно, но критично понимать:

Объект AОбъект BOnCollisionOnTrigger
Static colliderStatic collider
Static colliderDynamic RigidbodyДа (на обоих)Да, если isTrigger
Dynamic RigidbodyDynamic RigidbodyДа (на обоих)Да, если isTrigger
Kinematic RigidbodyStatic colliderДа, если isTrigger
Kinematic RigidbodyDynamic RigidbodyДаДа, если isTrigger
Kinematic RigidbodyKinematic RigidbodyДа, если isTrigger

Запомните главное: для OnCollisionEnter хотя бы один из объектов должен быть non-kinematic Rigidbody. Для OnTriggerEnter достаточно одного Rigidbody любого типа.

Layers и Layer Collision Matrix

У каждого GameObject есть Layer (0..31). В Project Settings → Physics → Layer Collision Matrix включаются/выключаются взаимодействия между слоями. Пример использования:

  • Слой Player не сталкивается с Trigger.
  • Слой Bullet сталкивается с Enemy, но не с другими Bullet.

Это эффективнее, чем фильтровать в OnCollisionEnter — PhysX даже не вызовет событие, если слои не взаимодействуют.

Raycast и физика

Уже видели в главе про ввод. Полный API:

// Простой raycast
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask)) { }

// Несколько попаданий
RaycastHit[] hits = Physics.RaycastAll(ray, maxDistance, layerMask);

// "Толстый" луч — SphereCast / CapsuleCast / BoxCast
Physics.SphereCast(origin, radius, direction, out hit, maxDistance);

// Не луч, а проверка области
Collider[] inSphere = Physics.OverlapSphere(center, radius, layerMask);

layerMask — это битовая маска слоёв, через которые луч проходит. Удобно с LayerMask.GetMask("Enemy", "Wall").

CharacterController vs Rigidbody для игрока

Для управления персонажем есть два подхода:

  1. CharacterController — специальный компонент с capsule-формой. Не использует Rigidbody, не реагирует на силы. У вас полный контроль через Move(). Идеален для шутеров от первого лица, платформеров — где нужна предсказуемость, а не симуляция.
  2. Rigidbody + CapsuleCollider — физический персонаж. Реалистичные столкновения, но требует аккуратной настройки damping, max-slope handling, anti-slide.
// CharacterController
public class FpsMove : MonoBehaviour
{
    private CharacterController _cc;
    private Vector3 _velocity;
    [SerializeField] private float speed = 5f;
    [SerializeField] private float gravity = -20f;

    private void Awake() => _cc = GetComponent<CharacterController>();

    private void Update() {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = transform.right * h + transform.forward * v;
        _cc.Move(move * speed * Time.deltaTime);

        if (_cc.isGrounded && _velocity.y < 0) _velocity.y = -2f;
        _velocity.y += gravity * Time.deltaTime;
        _cc.Move(_velocity * Time.deltaTime);
    }
}

Подробнее эту схему разберём в практической главе.

В следующей главе — камера и Cinemachine.