Ускорение, скорость и трение
Реальный герой не замирает мгновенно — он разгоняется и тормозит. Ускорение и трение делают управление живым и приятным.
Суть: ускорение меняет скорость, скорость меняет позицию. Добавив трение (умножение скорости на число меньше 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).
Итог: ускорение питает скорость, скорость питает позицию, трение тормозит. Эта цепочка превращает дёрганое движение в живое и отзывчивое.