Идеальный прыжок и секрет Coyote Time: почему одни платформеры ощущаются отзывчивыми, а другие нет

Коротко

Прыжок в 2D-платформере ощущается отзывчивым тогда, когда игра компенсирует человеческую задержку зрительно-моторной реакции и считает параметры физики от геометрии клеток уровня, а не от случайных констант. Пять приёмов — окно «времени койота» (Coyote Time, 80–120 мс), буферизация нажатия перед приземлением (Jump Buffering, 100–150 мс), асимметричная гравитация на спуске (1,6–2,2x), регулировка высоты удержанием кнопки и зависание в вершине прыжка — превращают «ватное» управление в точное. В статье приведены точные формулы перевода высоты в пикселях в скорость и гравитацию, таблица таймингов в миллисекундах и проверенный в Node.js код прыжка с окнами Coyote Time и буфером нажатия.

Содержание

  1. Почему честная школьная парабола ломает ощущение прыжка
  2. Как вывести скорость и гравитацию из высоты в пикселях и времени подъёма
  3. Секрет №1: Coyote Time («время койота») при сходе с края уступа
  4. Секрет №2: Jump Buffering (буферизация нажатия до касания земли)
  5. Секрет №3: Асимметричная гравитация, отсечение прыжка и зависание в вершине
  6. Эталонная таблица таймингов отзывчивого 2D-прыжка
  7. Практический сценарий: прыжок с Coyote Time и буфером на JavaScript
  8. Когда вам это не подойдёт

Почему честная школьная парабола ломает ощущение прыжка

Школьная формула свободного падения с постоянным ускорением 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).

Логика реализуется одной числовой переменной ТаймерКойота:

  1. Пока коллайдер персонажа касается земли, ТаймерКойота в каждом кадре приравнивается к 0.10 с (100 мс).
  2. Когда персонаж сбегает с края платформы (но не прыгал!), ТаймерКойота в каждом кадре уменьшается на шаг времени dt.
  3. Если игрок нажимает клавишу прыжка при ТаймерКойота > 0, прыжок срабатывает штатно, а ТаймерКойота немедленно обнуляется (0), чтобы исключить двойной прыжок в воздухе.

Секрет №2: Jump Buffering (буферизация нажатия до касания земли)

Jump Buffering (буфер прыжка) решает зеркальную ошибку при серии прыжков по узким столбикам, когда игрок нажимает кнопку прыжка за 50–120 мс до того, как ноги персонажа коснулись следующей платформы. Без буфера игра отвергает нажатие (так как в момент клика герой ещё в воздухе), и после приземления персонаж остаётся стоять на месте.

С буфером прыжка нажатие кнопки не проверяется только в один мгновенный кадр, а взводит переменную БуферПрыжка = 0.12 с (120 мс, или 7 кадров при 60 к/с):

  • В каждом кадре БуферПрыжка убывает на dt;
  • Как только срабатывает контакт с землёй (или активен ТаймерКойота > 0) и при этом БуферПрыжка > 0, персонаж мгновенно отталкивается вверх в первом же кадре приземления, а оба таймера сбрасываются в 0.

Секрет №3: Асимметричная гравитация, отсечение прыжка и зависание в вершине

Три дополнительных модификатора вертикальной скорости Vy дают игроку полный контроль над траекторией в воздухе:

  1. Ускоренное падение (Fall Multiplier): когда персонаж летит вниз (Vy > 0, если ось Y направлена вниз), базовая гравитация умножается на коэффициент 1.8 (g_down = 1,8 × g_up). Подъём на 112 пикселей занимает 350 мс, а спуск с той же высоты — около 260 мс.
  2. Раннее отсечение прыжка (Variable Jump Cutoff): если игрок слегка коснулся кнопки и отпустил её до достижения верхней точки (пока персонаж ещё движется вверх), вертикальная скорость умножается на 0.5 (или гравитация утраивается до конца подъёма). Это позволяет из одного базового прыжка получить и короткий подскок на 1 клетку (32 пикселя), и полный прыжок на 3,5 клетки (112 пикселей).
  3. Зависание в вершине (Apex Hang Time): когда модуль вертикальной скорости вблизи пика траектории мал (|Vy| < 60 пикс/с), гравитация временно снижается до 0.5x, а горизонтальная манёвренность увеличивается на 15–20%, давая игроку долю секунды на точное прицеливание при посадке на узкий уступ.

Эталонная таблица таймингов отзывчивого 2D-прыжка

В таблице сведены рабочие диапазоны параметров, которые можно сразу перенести в константы кода игры.

Рекомендуемые численные параметры прыжка для 2D-платформера (сетка 32×32 пикселя)
Параметр механикиЗначение в единицах времени / физикиЭквивалент в кадрах при 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 Time100 мс (0,10 с)6 кадров3 кадраПрощение позднего нажатия на краю пропасти
Окно Jump Buffering120 мс (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 игровой движок; он в разработке и публично пока не открыт, заявку можно оставить на странице раннего доступа (подача заявки не гарантирует приглашение).

Читайте также:

Частые вопросы

Что такое Coyote Time простыми словами?

Coyote Time («время койота») — это короткий интервал времени (обычно от 80 до 120 миллисекунд, или 5–7 кадров при 60 к/с) после того, как персонаж уже шагнул с края платформы в воздух, в течение которого игра всё ещё разрешает нажать кнопку и совершить прыжок.

Чем Jump Buffering отличается от Coyote Time?

Coyote Time помогает при запоздалом нажатии на краю пропасти при отталкивании, а Jump Buffering (буферизация прыжка) помогает при слишком раннем нажатии перед приземлением — игра запоминает нажатие на 100–150 мс и выполняет прыжок в первый же кадр касания земли.

Почему в хороших платформерах гравитация при падении сильнее, чем при взлёте?

При одинаковой гравитации подъём и спуск длятся поровну, из-за чего персонаж кажется «плывущим» в воде. Увеличение гравитации на спуске в 1,6–2,2 раза делает приземление быстрым и предсказуемым.

Как проверить тайминги прыжка, если частота кадров на разных устройствах разная?

Считайте все таймеры и скорости через шаг времени (deltaTime), а не в кадрах. Тогда окно Coyote Time 100 мс занимает 6 кадров при 60 к/с и 3 кадра при 30 к/с, но по времени остаётся одинаковым. Проверяйте прыжок на устройстве с самой низкой частотой кадров из целевых.