Коротко
Прыжок в 2D-платформере ощущается отзывчивым тогда, когда игра компенсирует человеческую задержку зрительно-моторной реакции и считает параметры физики от геометрии клеток уровня, а не от случайных констант. Пять приёмов — окно «времени койота» (Coyote Time, 80–120 мс), буферизация нажатия перед приземлением (Jump Buffering, 100–150 мс), асимметричная гравитация на спуске (1,6–2,2x), регулировка высоты удержанием кнопки и зависание в вершине прыжка — превращают «ватное» управление в точное. В статье приведены точные формулы перевода высоты в пикселях в скорость и гравитацию, таблица таймингов в миллисекундах и проверенный в Node.js код прыжка с окнами Coyote Time и буфером нажатия.
Содержание
- Почему честная школьная парабола ломает ощущение прыжка
- Как вывести скорость и гравитацию из высоты в пикселях и времени подъёма
- Секрет №1: Coyote Time («время койота») при сходе с края уступа
- Секрет №2: Jump Buffering (буферизация нажатия до касания земли)
- Секрет №3: Асимметричная гравитация, отсечение прыжка и зависание в вершине
- Эталонная таблица таймингов отзывчивого 2D-прыжка
- Практический сценарий: прыжок с Coyote Time и буфером на JavaScript
- Когда вам это не подойдёт
Почему честная школьная парабола ломает ощущение прыжка
Школьная формула свободного падения с постоянным ускорением g и строгим условием «прыгать можно только пока ноги касаются земли» создаёт в 2D-игре две проблемы: персонаж слишком медленно падает вниз и не срабатывает на краю пропасти.
Время простой зрительно-моторной реакции человека на визуальный стимул измеряется сотнями миллисекунд: медиана онлайн-теста Human Benchmark — 273 мс (источник: humanbenchmark.com/tests/reactiontime/statistics, проверено: октябрь 2026); при частоте 60 к/с, где один кадр длится 16,67 мс, это около 16 кадров. Когда игрок на скорости 300 пикселей/с бежит к обрыву и нажимает клавишу прыжка «в последний момент», его палец опускается на кнопку на 30–80 мс позже, чем нижняя грань коллайдера персонажа покинула край платформы. Строгая физическая проверка видит, что контакта с полом уже нет, игнорирует нажатие, и герой падает в яму, а игрок считает, что «кнопка не сработала».
Как вывести скорость и гравитацию из высоты в пикселях и времени подъёма
Профессиональные геймдизайнеры задают прыжок не подбором абстрактных сил в ньютонах, а двумя величинами, напрямую связанными с тайловой сеткой уровня: желаемой высотой прыжка H (в пикселях) и временем достижения верхней точки t_apex (в секундах). Этот математический метод представил инженер Кайл Питтман в докладе «Math for Game Programmers: Building a Better Jump» на конференции GDC 2016 (источник: GDC Vault, 2016 год, проверено: октябрь 2026).
Из кинематических уравнений движения с постоянным ускорением на участке подъёма (y(t) = v0 * t - 0.5 * g * t² и v(t_apex) = v0 - g * t_apex = 0) однозначно выражаются гравитация подъёма g_up и начальная вертикальная скорость прыжка v0:
g_up = (2 * H) / (t_apex * t_apex)
v0 = (2 * H) / t_apex
Конкретный расчёт под сетку 32×32 пикселя:
Пусть персонаж должен перепрыгивать стену высотой в 3 клетки с запасом в полклетки (H = 3,5 × 32 = 112 пикселей) и достигать вершины прыжка за t_apex = 0,35 с (21 кадр при 60 к/с). Подставляем значения в формулы:
g_up = (2 * 112) / (0.35 * 0.35) = 224 / 0.1225 ≈ 1 828,57 пикс/с²
v0 = (2 * 112) / 0.35 = 640 пикс/с
При таких числах герой гарантированно поднимется ровно на 112 пикселей за 350 мс, и левел-дизайнеру не придётся перестраивать уровни методом проб и ошибок.
Секрет №1: Coyote Time («время койота») при сходе с края уступа
Coyote Time («время койота», названное в честь мультипликационного койота Wile E. Coyote из серии Looney Tunes студии Warner Bros., впервые появившегося на экране в 1949 году, который продолжает бежать по воздуху, пока не посмотрит вниз) — это таймер длительностью 80–120 мс, разрешающий прыжок сразу после схода с платформы. В платформере Celeste (Maddy Makes Games, релиз 25 января 2018 года) окно Coyote Time составляет 6 кадров при 60 к/с, то есть ровно 100 мс (источник: константа JumpGraceTime = 0.1f в файле Player.cs открытого репозитория github.com/NoelFB/Celeste, опубликованного в 2020 году, проверено: октябрь 2026).
Логика реализуется одной числовой переменной ТаймерКойота:
- Пока коллайдер персонажа касается земли,
ТаймерКойотав каждом кадре приравнивается к0.10с (100 мс). - Когда персонаж сбегает с края платформы (но не прыгал!),
ТаймерКойотав каждом кадре уменьшается на шаг времениdt. - Если игрок нажимает клавишу прыжка при
ТаймерКойота > 0, прыжок срабатывает штатно, аТаймерКойотанемедленно обнуляется (0), чтобы исключить двойной прыжок в воздухе.
Секрет №2: Jump Buffering (буферизация нажатия до касания земли)
Jump Buffering (буфер прыжка) решает зеркальную ошибку при серии прыжков по узким столбикам, когда игрок нажимает кнопку прыжка за 50–120 мс до того, как ноги персонажа коснулись следующей платформы. Без буфера игра отвергает нажатие (так как в момент клика герой ещё в воздухе), и после приземления персонаж остаётся стоять на месте.
С буфером прыжка нажатие кнопки не проверяется только в один мгновенный кадр, а взводит переменную БуферПрыжка = 0.12 с (120 мс, или 7 кадров при 60 к/с):
- В каждом кадре
БуферПрыжкаубывает наdt; - Как только срабатывает контакт с землёй (или активен
ТаймерКойота > 0) и при этомБуферПрыжка > 0, персонаж мгновенно отталкивается вверх в первом же кадре приземления, а оба таймера сбрасываются в0.
Секрет №3: Асимметричная гравитация, отсечение прыжка и зависание в вершине
Три дополнительных модификатора вертикальной скорости Vy дают игроку полный контроль над траекторией в воздухе:
- Ускоренное падение (Fall Multiplier): когда персонаж летит вниз (
Vy > 0, если осьYнаправлена вниз), базовая гравитация умножается на коэффициент1.8(g_down = 1,8 × g_up). Подъём на112пикселей занимает350 мс, а спуск с той же высоты — около260 мс. - Раннее отсечение прыжка (Variable Jump Cutoff): если игрок слегка коснулся кнопки и отпустил её до достижения верхней точки (пока персонаж ещё движется вверх), вертикальная скорость умножается на
0.5(или гравитация утраивается до конца подъёма). Это позволяет из одного базового прыжка получить и короткий подскок на 1 клетку (32пикселя), и полный прыжок на 3,5 клетки (112пикселей). - Зависание в вершине (Apex Hang Time): когда модуль вертикальной скорости вблизи пика траектории мал (
|Vy| < 60 пикс/с), гравитация временно снижается до0.5x, а горизонтальная манёвренность увеличивается на15–20%, давая игроку долю секунды на точное прицеливание при посадке на узкий уступ.
Эталонная таблица таймингов отзывчивого 2D-прыжка
В таблице сведены рабочие диапазоны параметров, которые можно сразу перенести в константы кода игры.
| Параметр механики | Значение в единицах времени / физики | Эквивалент в кадрах при 60 к/с (16,67 мс) | Эквивалент в кадрах при 30 к/с (33,33 мс) | За что отвечает в ощущениях игрока |
|---|---|---|---|---|
Высота полного прыжка (H) | 112 пикселей (3,5 клетки) | — | — | Преодоление препятствий в 3 клетки высотой с запасом 16 пикселей |
Время до вершины (t_apex) | 350 мс (0,35 с) | 21 кадр | 10,5 кадра | Темп взлёта: без ощущения тяжести и без «плавания» |
Начальная скорость (v0) | 640 пикс/с | 10,67 пикс/кадр | 21,33 пикс/кадр | Мгновенный отрыв от поверхности |
Гравитация подъёма (g_up) | 1 829 пикс/с² | — | — | Равномерное замедление до вершины траектории |
| Множитель гравитации падения | 1,8x (3 292 пикс/с²) | ~15,6 кадра на спуск | ~7,8 кадра на спуск | Быстрое и плотное приземление |
| Окно Coyote Time | 100 мс (0,10 с) | 6 кадров | 3 кадра | Прощение позднего нажатия на краю пропасти |
| Окно Jump Buffering | 120 мс (0,12 с) | 7 кадров | 3,6 кадра | Срабатывание прыжка, нажатого чуть раньше касания пола |
| Предельная скорость падения | 720 пикс/с | 12 пикс/кадр | 24 пикс/кадр | Защита от пролёта сквозь тонкие платформы в глубоких шахтах |
Практический сценарий: прыжок с Coyote Time и буфером на JavaScript
Прыжок с окнами Coyote Time (100 мс) и Jump Buffering (120 мс) укладывается в одну функцию шага физики с двумя таймерами, а проверить окна можно без графики — прогоном шагов по 1/60 с. Код ниже проверен в Node.js 24; комментарии показывают его фактический вывод.
// Параметры из таблицы: высота 112 пикс, подъём 0,35 с, ось Y направлена вниз
const H = 112, T_APEX = 0.35;
const G_UP = (2 * H) / (T_APEX * T_APEX); // ≈ 1828,57 пикс/с²
const V0 = (2 * H) / T_APEX; // 640 пикс/с
const FALL_MULT = 1.8, MAX_FALL = 720;
const COYOTE = 0.10, BUFFER = 0.12;
function createJumper() {
return { vy: 0, grounded: true, coyote: COYOTE, buffer: 0, jumped: false };
}
// Один шаг физики длиной dt; pressed — нажатие в этом шаге, released — отпускание
function stepJump(p, dt, pressed, released) {
if (pressed) p.buffer = BUFFER;
p.coyote = p.grounded ? COYOTE : Math.max(0, p.coyote - dt);
p.buffer = Math.max(0, p.buffer - (pressed ? 0 : dt));
if (p.buffer > 0 && p.coyote > 0) {
p.vy = -V0; p.grounded = false; p.coyote = 0; p.buffer = 0; p.jumped = true;
}
if (released && p.vy < 0) p.vy *= 0.5; // короткий прыжок
if (!p.grounded) {
const g = p.vy > 0 ? G_UP * FALL_MULT : G_UP; // ускоренное падение
p.vy = Math.min(MAX_FALL, p.vy + g * dt);
}
}
// Проверка 1: игрок сошёл с края и нажал прыжок через N мс
function lateJump(ms) {
const p = createJumper(), dt = 1 / 60;
p.grounded = false; // шагнул в пропасть
const frames = Math.round(ms / (1000 * dt));
for (let i = 0; i < frames; i++) stepJump(p, dt, false, false);
stepJump(p, dt, true, false);
return p.jumped;
}
console.log("нажал через 83 мс:", lateJump(83)); // true — сработало окно койота
console.log("нажал через 150 мс:", lateJump(150)); // false — окно закрылось
// Проверка 2: игрок нажал прыжок за 100 мс до касания земли
const q = createJumper(), dt = 1 / 60;
q.grounded = false; q.vy = 300;
stepJump(q, dt, true, false); // раннее нажатие в воздухе
for (let i = 0; i < 5; i++) stepJump(q, dt, false, false); // ещё ~83 мс полёта
q.grounded = true; q.vy = 0; // приземление
stepJump(q, dt, false, false);
console.log("буфер сработал при приземлении:", q.jumped); // true
Оба таймера уменьшаются на шаг времени dt, а не на 1 за кадр, поэтому при 30 к/с окно Coyote Time остаётся 100 мс, хотя занимает уже 3 кадра. Чтобы проверить свой прыжок, меняйте в lateJump задержку с шагом 16 мс и найдите последнюю, при которой прыжок ещё срабатывает.
Когда вам это не подойдёт
Упреждающие буферы и асимметричная гравитация подходят для аркадных и скоростных платформеров, но вредят в следующих жанрах и задачах:
- Кинематографические платформеры с инерционной анимацией шага: в играх вроде оригинального Prince of Persia (Джордан Мехнер, Brøderbund, 1989 год, проверено: октябрь 2026) или Another World (Эрик Шайи, Delphine Software, 1991 год) задержка перед толчком намеренно привязана к фазе разбега и сгибания коленей персонажа. Добавление Coyote Time разрушит там чувство веса и риска.
- Физические головоломки с честным законом сохранения энергии: если механика игры требует, чтобы тело после падения с высоты
Hподскакивало на батуте строго на ту же высотуH, разная гравитация на подъёме (g_up) и спуске (1,8 × g_up) нарушит симметрию скорости и сломает физическую загадку. - Пошаговые и клеточные игры: если персонаж прыгает ровно на одну клетку за ход, как в пошаговых головоломках, окна в миллисекундах не нужны — ввод обрабатывается по ходам, а не по кадрам.
Мы разрабатываем J2DRU — браузерный 2D игровой движок; он в разработке и публично пока не открыт, заявку можно оставить на странице раннего доступа (подача заявки не гарантирует приглашение).
Читайте также: