Ускорение, скорость и трение

Реальный герой не замирает мгновенно — он разгоняется и тормозит. Ускорение и трение делают управление живым и приятным.
Суть: ускорение меняет скорость, скорость меняет позицию. Добавив трение (умножение скорости на число меньше 1), получаем плавный разгон и торможение.

Если герой мгновенно набирает максимальную скорость и так же резко встаёт — управление ощущается «деревянным». В хороших играх движение инерционное: нажал — герой плавно разгоняется, отпустил — плавно тормозит. Этого добиваются через ускорение и трение, и это снова чистая физика, доступная без всякой графики.

Цепочка простая и красивая. Ускорение (acceleration) — это насколько меняется скорость за секунду. Скорость (velocity) — насколько меняется позиция. Каждый кадр: к скорости прибавляем ускорение × dt, к позиции прибавляем скорость × dt. Нажата клавиша — даём ускорение в нужную сторону. Отпущена — ускорения нет.

Чтобы герой не разгонялся бесконечно и тормозил при отпускании, добавляют трение: каждый кадр умножают скорость на число чуть меньше единицы, например 0.9. Скорость постепенно затухает к нулю. Чем ближе множитель к 1, тем «скользче» герой (как на льду); чем меньше — тем резче тормозит.

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

Это двухступенчатая передача: ускорение питает скорость, скорость питает позицию. Трение — тормоз на скорости:

   ввод -> УСКОРЕНИЕ (a)
              |  velocity += a * dt
              v
           СКОРОСТЬ (v)
              |  velocity *= 0.9   (трение)
              |  position += v * dt
              v
           ПОЗИЦИЯ (p)  -> рисуем

В pygame (читаем):

pos = pygame.math.Vector2(400, 300)
vel = pygame.math.Vector2(0, 0)
ACCEL = 2000     # пикс/сек^2
FRICTION = 0.85

keys = pygame.key.get_pressed()
acc = pygame.math.Vector2(0, 0)
if keys[pygame.K_LEFT]:  acc.x = -ACCEL
if keys[pygame.K_RIGHT]: acc.x = ACCEL
vel += acc * dt
vel *= FRICTION          # трение
pos += vel * dt

Эту физику можно полностью проверить без графики. Подадим ускорение пару кадров, потом отпустим и посмотрим, как трение гасит скорость. Попробуй сам:

ACCEL = 2000.0      # пикс/сек^2
FRICTION = 0.85
dt = 0.1

vel = 0.0
pos = 0.0

# 3 кадра жмём вправо, потом 3 кадра отпускаем
inputs = [ACCEL, ACCEL, ACCEL, 0, 0, 0]
for frame, acc in enumerate(inputs, 1):
    vel += acc * dt
    vel *= FRICTION         # трение гасит скорость
    pos += vel * dt
    print(f"кадр {frame}: скорость={vel:6.1f}  позиция={pos:6.1f}")

Чувство веса и характер персонажа

Параметры ускорения и трения — это не сухие числа, а характер твоего героя. Большое ускорение и сильное трение дают «цепкого» персонажа, который мгновенно слушается и резко встаёт — так управляется Mario. Маленькое ускорение и слабое трение дают «скользкого» героя с разгоном и заносами — так ощущается катание по льду или полёт в космосе. Меняя всего два числа, ты задаёшь совершенно разное ощущение игры, и это один из самых мощных инструментов гейм-фила.

Поэтому не подбирай эти значения наугад один раз и навсегда. Выведи их в константы вверху файла и поиграй: сделай трение 0.7, потом 0.95, почувствуй разницу руками. Хорошие разработчики тратят часы на «настройку ощущения» персонажа, потому что именно оно решает, приятно ли держать в руках управление. Цифры, которые ты крутил в запускаемом примере выше, — это и есть та самая настройка, только без графики. Понимание физики даёт тебе власть над характером героя.

Когда введёшь инерцию, почти наверняка захочется потолок скорости — иначе при долгом разгоне герой станет неуправляемо быстрым. Ограничить просто: после прибавления ускорения проверь длину вектора скорости и, если она больше максимума, укороти его до максимума (у Vector2 для этого есть clamp_magnitude). Тогда герой разгоняется плавно, но не превышает заданного предела. Сочетание «ускорение плюс трение плюс потолок скорости» — это, по сути, мини-физический движок, и он одинаково хорошо описывает и бегущего человечка, и дрейфующий в космосе корабль. Меняя три числа, ты получаешь совершенно разный характер движения, не трогая ни строчки остального кода.

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

  • Прибавлять ускорение прямо к позиции — пропускаешь ступень скорости, движение получается неестественным.
  • Трение больше или равно 1 — герой не затормозит никогда, разгон бесконечный.
  • Забыть dt у ускорения — снова привязка к FPS.

Best practices

  • Соблюдай порядок: ускорение → скорость → позиция.
  • Подбирай трение экспериментом: 0.8 — резко, 0.95 — скользко.
  • Ограничивай максимальную скорость, если нужен потолок (vel.clamp_magnitude).

Итог: ускорение питает скорость, скорость питает позицию, трение тормозит. Эта цепочка превращает дёрганое движение в живое и отзывчивое.

Проверьте себя
1. В каком порядке связаны ускорение, скорость и позиция?
Aпозиция -> скорость -> ускорение
Bускорение -> скорость -> позиция
Cвсё одновременно из одного числа
Dскорость -> ускорение -> позиция
2. Что произойдёт при трении (множителе скорости) равном 1.0?
AГерой резко тормозит
BСкорость не затухает — разгон бесконечный
CИгра вылетит
DГерой телепортируется