Физика — Rigidbody, Collider и столкновения
Как двигать объекты физикой и реагировать на столкновения.
Unity использует PhysX от NVIDIA для 3D-физики (для 2D — Box2D). Это означает: симуляция твёрдых тел, столкновения, силы, joints. Всё это работает из коробки, ваша задача — корректно настроить компоненты.
Три класса участников
- Static — GameObject с Collider, без Rigidbody. Не движется, идеален для стен, пола, статичной геометрии.
- Kinematic — Rigidbody с
isKinematic = true. Двигается только через ваш скрипт (черезMovePosition/MoveRotation), не реагирует на силы. - Dynamic — обычный Rigidbody. Реагирует на гравитацию, силы, столкновения.
Если у объекта нет 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— управляется ли только скриптами.interpolation—None/Interpolate/Extrapolate. Сглаживает движение между шагами физики. Для игрока обычно ставятInterpolate.collisionDetectionMode—Discrete/Continuous/ContinuousDynamic/ContinuousSpeculative. Для быстрых объектов (пуль, мячей) важноContinuous-варианты, чтобы избежать “проскакивания” сквозь тонкие коллайдеры.
В 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 | Объект B | OnCollision | OnTrigger |
|---|---|---|---|
| Static collider | Static collider | — | — |
| Static collider | Dynamic Rigidbody | Да (на обоих) | Да, если isTrigger |
| Dynamic Rigidbody | Dynamic Rigidbody | Да (на обоих) | Да, если isTrigger |
| Kinematic Rigidbody | Static collider | — | Да, если isTrigger |
| Kinematic Rigidbody | Dynamic Rigidbody | Да | Да, если isTrigger |
| Kinematic Rigidbody | Kinematic 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 для игрока
Для управления персонажем есть два подхода:
- CharacterController — специальный компонент с capsule-формой. Не использует Rigidbody, не
реагирует на силы. У вас полный контроль через
Move(). Идеален для шутеров от первого лица, платформеров — где нужна предсказуемость, а не симуляция. - 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.