---
title: "Контрольные точки в 2D-игре: где ставить и сколько стоит ошибка"
description: "Как расставить контрольные точки (чекпойнты) в 2D-уровне по расчёту, а не на глаз: формула ожидаемых потерь времени от длины участка, шанса пройти его и времени возврата, жадная расстановка точек по бюджету потерь, что откатывать при гибели и пример на JavaScript, проверенный в Node.js 24."
slug: kontrolnye-tochki-v-2d-igre-gde-stavit-i-cena-oshibki
date: 2026-11-05
updated: 2026-11-05
scheduled_at: "2026-11-05T14:00:00+03:00"
rubric: gejmdizajn-i-mehaniki
tags: ["kontrolnye-tochki", "dizajn-urovnej", "gejmdizajn", "platformer", "javascript"]
reading_time: 8
draft: false
summary: "Цена ошибки в 2D-игре — это время, которое игрок тратит на повтор уже пройденного. Её можно посчитать: при длине участка L секунд, шансе пройти его с одной попытки p и времени возврата R ожидаемое время прохождения равно L + (1/p − 1) × (L/2 + R). Уровень на 75 секунд из шести испытаний без контрольных точек обходится среднему игроку в 261 секунду потерь, а с четырьмя точками — меньше 30. Точки ставят перед трудными испытаниями так, чтобы ожидаемые потери на каждом участке не превышали бюджет, а при гибели откатывают всё, что игрок собрал после точки."
---

## Коротко

Цена ошибки — это время повтора уже пройденного, и её можно посчитать по длине участка, шансу пройти его и времени возврата. Контрольные точки ставят перед трудными испытаниями так, чтобы ожидаемые потери на каждом участке укладывались в бюджет, а при гибели откатывают всё, что изменилось после точки. Ниже — формула, таблица потерь, жадная расстановка точек и код на JavaScript, проверенный в Node.js 24.

## Содержание

1. Цена ошибки: формула ожидаемого времени
2. Таблица: сколько стоит участок
3. Шанс пройти участок из нескольких испытаний
4. Расстановка точек по бюджету потерь
5. Что откатывать при гибели
6. Код на JavaScript
7. Практический сценарий: разметка уровня за 30 минут
8. Когда вам это не подойдёт

## Цена ошибки: формула ожидаемого времени

Ожидаемое время прохождения участка зависит от трёх чисел: длины L в секундах, шанса пройти с одной попытки p и времени возврата R к контрольной точке. Если неудача в среднем случается на середине участка, то:

```text
среднее число неудач = 1/p − 1
ожидаемое время      = L + (1/p − 1) × (L/2 + R)
потери               = ожидаемое время − L
```

Допущение «неудача в середине» грубое: на участке с одним трудным местом в конце игрок теряет почти всё L. Тогда в формуле вместо L/2 ставят расстояние до трудного места. Шанс p оценивают по плейтесту: доля попыток, которые дошли до следующей точки.

## Таблица: сколько стоит участок

Цифры ниже посчитаны по формуле выше; они показывают, как быстро растут потери при длинном участке и низком шансе.

| Длина участка L | Шанс p | Возврат R | Ожидаемое время | Потери |
|---|---|---|---|---|
| 30 с | 50% | 2 с | 47 с | 17 с |
| 60 с | 50% | 2 с | 92 с | 32 с |
| 60 с | 25% | 2 с | 156 с | 96 с |
| 30 с | 25% | 5 с | 90 с | 60 с |

Удвоение длины при том же шансе почти удваивает потери, а падение шанса с 50% до 25% утраивает число неудач. Поэтому длинный участок с трудным финалом — главный источник раздражения.

## Шанс пройти участок из нескольких испытаний

Шанс пройти участок равен произведению шансов его испытаний. Пропасть с шансом 90% и шипы с шансом 80% дают вместе 72%, а если добавить стража с 70%, — уже 50%. Каждое следующее испытание без контрольной точки делает весь участок заметно труднее, даже если по отдельности испытания лёгкие.

Если весь уровень из шести испытаний длиной 75 секунд оставить без точек, шанс пройти его целиком — около 13%, а ожидаемые потери — 261 секунда при возврате за 1 с.

## Расстановка точек по бюджету потерь

Точки удобно расставлять жадно: добавлять испытания в текущий участок, пока ожидаемые потери не превысят бюджет, и ставить точку перед испытанием, которое бюджет превысило. Бюджет — 10–15 секунд для обычного уровня и больше для финального испытания, если трудность — часть замысла.

Для уровня из примера ниже с бюджетом 15 секунд и возвратом 1 с точки встают в начале, перед стражем, перед пилой и перед финалом. Потери на участках — 3,9, 9,9, 4,0 и 11,0 секунды, всего 28,8 секунды вместо 261.

## Что откатывать при гибели

При активации точки сохраняют снимок того, что игрок может изменить, а при гибели восстанавливают его. В снимок входят собранные предметы, счёт, открытые двери и убитые враги. Без отката появляется лазейка: можно собрать монеты, нарочно погибнуть и собрать их снова.

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

## Код на JavaScript

Код считает ожидаемое время, расставляет точки по бюджету и откатывает состояние при гибели. Он проверен в Node.js 24, числа в комментариях совпадают с выводом.

```js
// Ожидаемое время прохождения участка длиной L секунд,
// если попытка удаётся с вероятностью p, неудача случается в среднем на середине,
// а возврат к контрольной точке занимает R секунд
function expectedTime(L, p, R) {
  const fails = 1 / p - 1; // среднее число неудач до успеха
  return L + fails * (L / 2 + R);
}

// Успех на участке из нескольких испытаний — произведение вероятностей
function segmentChance(challenges) {
  return challenges.reduce((acc, c) => acc * c.p, 1);
}

// Расставляем контрольные точки жадно: добавляем испытания в участок,
// пока ожидаемые потери времени не превысят бюджет
function placeCheckpoints(challenges, R, lossBudget) {
  const cps = [0];
  let seg = [];
  for (let i = 0; i < challenges.length; i++) {
    const trial = seg.concat(challenges[i]);
    const L = trial.reduce((s, c) => s + c.len, 0);
    const loss = expectedTime(L, segmentChance(trial), R) - L;
    if (seg.length > 0 && loss > lossBudget) {
      cps.push(i); // точка перед испытанием i
      seg = [challenges[i]];
    } else {
      seg = trial;
    }
  }
  return cps;
}

// Снимок при активации точки и откат при гибели
function makeCheckpointSystem(world) {
  let snap = null;
  return {
    activate(id) {
      snap = { id, coins: [...world.coins], score: world.score, opened: [...world.opened] };
    },
    respawn() {
      world.coins = [...snap.coins];
      world.score = snap.score;
      world.opened = [...snap.opened];
      return snap.id;
    },
  };
}

// Проверки
console.log(expectedTime(30, 0.5, 2).toFixed(1)); // 47.0
console.log(expectedTime(60, 0.5, 2).toFixed(1)); // 92.0
console.log(expectedTime(60, 0.25, 2).toFixed(1)); // 156.0
console.log(expectedTime(30, 0.25, 5).toFixed(1)); // 90.0

const level = [
  { name: 'пропасть', len: 8, p: 0.9 },
  { name: 'шипы', len: 10, p: 0.8 },
  { name: 'страж', len: 15, p: 0.7 },
  { name: 'лифт', len: 12, p: 0.85 },
  { name: 'пила', len: 10, p: 0.6 },
  { name: 'финал', len: 20, p: 0.5 },
];
console.log(placeCheckpoints(level, 1, 15).map((i) => level[i].name).join(', '));
// пропасть, страж, пила, финал

const world = { coins: ['a'], score: 100, opened: [] };
const cp = makeCheckpointSystem(world);
cp.activate('у лифта');
world.coins.push('b'); world.score += 50; world.opened.push('дверь 2');
console.log(cp.respawn(), world.coins, world.score, world.opened); // у лифта [ 'a' ] 100 []
```

Точка «у лифта» восстанавливает монеты, счёт и закрытую дверь — собранное после неё пропадает.

## Практический сценарий: разметка уровня за 30 минут

Расставить точки на готовом уровне можно за 5 шагов:

1. **Список испытаний.** Выпишите испытания по порядку и время прохождения каждого без ошибок, например по записи своего прохождения.
2. **Шансы.** Дайте уровень 3–5 игрокам и посчитайте долю удачных попыток на каждом испытании; без плейтеста возьмите оценку и пометьте её.
3. **Бюджет.** Выберите бюджет потерь на участок: 10–15 секунд для обычных уровней.
4. **Расстановка.** Запустите `placeCheckpoints` и поставьте точки перед найденными испытаниями; проверьте, что точка стоит на безопасной земле, а не перед прыжком вслепую.
5. **Возврат.** Измерьте время от гибели до управления героем; если больше 2 секунд, сократите анимацию гибели или затемнение.

## Когда вам это не подойдёт

Расчёт по формуле полезен для линейных уровней с испытаниями, но не для всех игр:

- **Рогалики с постоянной смертью:** потеря прогресса — часть жанра, вместо точек используют короткие забеги и мета-прогрессию.
- **Головоломки:** вместо возврата к точке нужен откат на один ход или перезапуск комнаты.
- **Открытые миры:** игрок сам выбирает маршрут, поэтому точки заменяют частыми автосохранениями и точками быстрого перемещения.

Мы разрабатываем **J2DRU** — браузерный 2D игровой движок; хранение в браузере входит в проверенные внутри разработки основы. Движок в разработке и публично пока не открыт, заявку можно оставить на [странице раннего доступа](/early-access?utm_source=blog&utm_medium=article&utm_campaign=kontrolnye-tochki-v-2d-igre-gde-stavit-i-cena-oshibki) (подача заявки не гарантирует приглашение).

**Источники:** внешних фактов в статье нет; формулы, таблица и числа примеров — наш расчёт, проверенный в Node.js 24.

**Читайте также:**
- [Сохранения в браузерной игре: localStorage или IndexedDB](/blog/sohraneniya-v-brauzernoj-igre-localstorage-indexeddb)
- [Патрулирование врагов и читаемые опасности](/blog/patrulirovanie-vragov-i-chitaemye-opasnosti)
- [Идеальный прыжок и секрет Coyote Time](/blog/idealnyj-pryzhok-i-sekret-coyote-time)
