Rigidbody2D: гравитация и движение по физике

Хочешь, чтобы герой падал, прыгал, отскакивал? Добавь Rigidbody2D — компонент, который отдаёт объект во власть физического движка.

Суть: Rigidbody2D делает объект физическим телом: на него действует гравитация, он имеет скорость и массу. Есть три типа: Dynamic (полная физика), Kinematic (двигается кодом, но участвует в столкновениях), Static (неподвижный, для стен). Физическое движение делают в FixedUpdate.

До этого мы двигали объект, напрямую меняя transform.position. Но реальная игровая физика — гравитация, инерция, отскоки — так не делается. Для неё есть Rigidbody2D. Добавил его на объект — и движок Box2D начинает симулировать тело: оно падает под гравитацией, копит скорость, сталкивается с другими.

Три типа тел решают разные задачи:

  • Dynamic — полноценное физическое тело. Падает, его можно толкать силами. Герой, ящики, мячи.
  • Kinematic — не подчиняется гравитации и силам, но двигается кодом и участвует в столкновениях. Платформы, движущиеся преграды.
  • Static — вообще не двигается, экономит ресурсы. Стены, земля, статичные препятствия.

Как работает под капотом

Каждый шаг физики (в FixedUpdate) движок берёт скорость тела, двигает его, применяет гравитацию, проверяет столкновения:

Шаг физики (Dynamic тело):

  velocity.y += gravity * fixedDeltaTime   (гравитация копит скорость вниз)
  position   += velocity * fixedDeltaTime  (тело двигается)
  проверить столкновения, оттолкнуть       (не дать пройти сквозь пол)

Двигать Rigidbody напрямую через transform.position — ошибка: это «телепорт» мимо физики. Правильно менять velocity или применять силы AddForce. И только в FixedUpdate, ведь физика идёт фиксированным шагом.

using UnityEngine;

public class Jumper : MonoBehaviour
{
    [SerializeField] private float jumpForce = 7f;
    private Rigidbody2D rb;

    void Awake() { rb = GetComponent<Rigidbody2D>(); }

    void Update()
    {
        if (Input.GetButtonDown("Jump"))
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
    }
}

Симулируем падение под гравитацией на чистом Python — это та же интеграция скорости и позиции, что внутри Box2D:

# Тело падает под гравитацией; шаг физики фиксирован
gravity = -9.8
fixed_dt = 1 / 50            # 50 шагов физики в секунду
vy = 0.0                    # вертикальная скорость
y = 10.0                    # стартовая высота

step = 0
while y > 0 and step < 100:
    vy += gravity * fixed_dt   # гравитация копит скорость вниз
    y  += vy * fixed_dt        # двигаем тело
    step += 1
    if step % 10 == 0:
        print(f"шаг {step}: высота {y:.2f}, скорость {vy:.2f}")

print(f"Упал на землю за {step} шагов физики")

Та же логика на Python ▶ — гравитация не двигает тело сразу, она копит скорость, а уже скорость двигает тело. Поэтому падение ускоряется. Ровно это делает Rigidbody2D каждый шаг FixedUpdate.

Частые ошибки

  • Двигать Dynamic-тело через transform.position. Это конфликтует с физикой: объект дёргается и проходит сквозь стены. Меняй velocity или AddForce.
  • Применять силы в Update. Физику трогают в FixedUpdate. В Update только читают ввод и ставят флаг.
  • Стены без Static. Если земля — Dynamic-тело, она сама упадёт под гравитацией. Стены и пол делай Static.
  • Кэшировать Rigidbody через GetComponent каждый кадр. Получи ссылку один раз в Awake и храни.

Best practices

  • Получай Rigidbody2D один раз в Awake через GetComponent и сохраняй в поле.
  • Подвижный герой — Dynamic, движущиеся платформы — Kinematic, статичная геометрия — Static.
  • Прыжок: читай нажатие в Update, но меняй velocity аккуратно; для длительных сил используй AddForce в FixedUpdate.

Итоги: Rigidbody2D отдаёт объект физическому движку. Типы тел — Dynamic, Kinematic, Static — под разные роли. Физику двигают через velocity и AddForce в FixedUpdate, а не через transform.position. Гравитация копит скорость, а скорость двигает тело — отсюда ускорение падения.

Continuous Collision Detection: почему пуля пролетает сквозь стену

У дискретной физики есть коварная ловушка. Между двумя шагами симуляции быстрый объект — пуля, прыгающий герой на большой скорости — может за один шаг переместиться сразу за тонкую стену, так и не оказавшись внутри неё ни на одном кадре. Движок просто не заметил столкновения: вот объект перед стеной, а вот уже за ней. Это называется туннелированием. Лекарство — режим Continuous в настройке Collision Detection у Rigidbody2D: вместо проверки только в точках-кадрах движок проверяет весь путь объекта между ними, как непрерывную линию. Continuous дороже по расчётам, поэтому включай его только там, где он нужен — для быстрых пуль и стремительных персонажей, а не для всех тел подряд. Для медленных объектов хватает обычного дискретного режима.

Проверьте себя
1. Какой тип Rigidbody2D подходит для неподвижных стен и пола?
ADynamic
BKinematic
CStatic
DЛюбой
2. Как правильно двигать Dynamic-тело с физикой?
AМеняя transform.position каждый кадр
BЧерез velocity или AddForce в FixedUpdate
CЧерез Debug.Log
DНикак, оно двигается само