---
title: "Патрулирование врагов и читаемые опасности: как настроить честные столкновения и окно предупреждения"
description: "Как спроектировать честных врагов и ловушки в 2D-игре: три фазы атаки с замахом 250–400 мс под время реакции человека, математика патрулирования с паузой на краю, прощающий хитбокс шипов на 20–30% меньше спрайта и проверенный код стража на JavaScript."
slug: patrulirovanie-vragov-i-chitaemye-opasnosti
date: 2026-10-31
updated: 2026-11-02
scheduled_at: "2026-10-31T14:00:00+03:00"
rubric: gejmdizajn-i-mehaniki
tags: ["gejmdizajn", "mehaniki", "hitboksy", "platformer", "novichkam"]
reading_time: 10
draft: false
summary: "Враги и ловушки в 2D-игре воспринимаются честными, когда соблюдены три инженерных правила: перед каждым ударом враг показывает заметный замах (телеграф) длительностью не менее 250–400 мс (чтобы игрок со средней зрительной реакцией около 273 мс успел ответить), маршрут патрулирования включает короткую паузу в точках разворота, а опасная зона шипов и снарядов делается на 20–30% меньше видимой картинки в пользу игрока."
---

## Коротко

Честное испытание в 2D-игре отличается от нечестного тремя измеримыми параметрами: у каждой атаки врага есть фаза заметного замаха (телеграфа) длительностью `250–400 мс`, у патрульного маршрута между точками `A` и `B` есть короткая пауза перед разворотом (`400–600 мс`), а физическая зона поражения шипов и ловушек уменьшена на `20–30%` относительно видимого спрайта. Такой запас учитывает время зрительно-моторной реакции человека и погрешность прямоугольных коллайдеров. В статье разобраны формулы патруля и усечённых хитбоксов, таблица фаз удара в миллисекундах и проверенный в Node.js код врага с фазами замаха, удара и восстановления.

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

1. Три фазы честной атаки: почему замах должен длиться минимум 250–400 мс
2. Математика патрулирования между точками A и B с паузой на краю
3. Прощающий хитбокс: почему зону шипов уменьшают на 20–30%
4. Контраст и форма: как отличить опасность от декорации за долю секунды
5. Практический сценарий: патрульный страж с предупреждением перед выпадом на JavaScript
6. Когда вам это не подойдёт

## Три фазы честной атаки: почему замах должен длиться минимум 250–400 мс

Любая читаемая атака врага или срабатывание ловушки в 2D-игре делится на три последовательные фазы: **замах (предупреждение / телеграф)**, **активный удар** и **восстановление (откат)**, причём фаза замаха не может быть короче `250 мс`.

Нижняя граница длительности замаха прямо следует из физиологии человека и задержки ввода:
- Медианное время простой зрительно-моторной реакции человека на смену цвета экрана в онлайн-выборке *Human Benchmark* составляет `273 мс` (около `16` кадров при `60 к/с`; источник: статистика `humanbenchmark.com/tests/reactiontime/statistics`, проверено: октябрь 2026);
- В реальной игре человеку нужно не просто нажать единственную кнопку, а распознать позу врага и выбрать действие (прыжок, шаг назад или удар) — по закону Хика—Хаймана (У. Э. Хик, *«On the rate of gain of information»*, Quarterly Journal of Experimental Psychology, 1952 год, проверено: октябрь 2026) время реакции с выбором из `2–3` вариантов возрастает до `300–450 мс`.

В классической книге по диснеевской анимации Фрэнка Томаса и Олли Джонстона *«The Illusion of Life: Disney Animation»* (1981 год, проверено: октябрь 2026) эта подготовительная фаза описана как принцип **Anticipation** (упреждение / отказное движение): перед рывком вперёд фигура сначала отводится назад, предупреждая зрителя о направлении и силе действия.

Таблица: Тайминги трёх фаз атаки врага в 2D-экшене и платформере (при 60 к/с, шаг 16,67 мс)

| Фаза атаки | Длительность в мс | Число кадров при 60 к/с | Активен ли хитбокс урона? | Что видит и слышит игрок |
|---|---|---|---|---|
| 1. Замах (`telegraph`) | `300–450 мс` | `18–27` кадров | **Нет** | Враг останавливается, отводит оружие назад, звучит предупреждающий сигнал |
| 2. Активный удар (`strike`) | `100–150 мс` | `6–9` кадров | **Да** | Быстрый выпад вперёд, чёткий след удара |
| 3. Восстановление (`recovery`) | `400–600 мс` | `24–36` кадров | **Нет** | Враг открыт для ответного прыжка сверху или обхода |

Если включить хитбокс урона сразу в кадре №1 фазы замаха, игрок получит урон от занесённой назад руки врага и воспримет столкновение как ошибку игры.

## Математика патрулирования между точками A и B с паузой на краю

Патрулирование — это циклическое движение врага между левой границей `minX` (точка `A`) и правой границей `maxX` (точка `B`) с фиксированной скоростью `speed` и обязательной паузой `waitTime` при достижении края.

Без паузы на развороте координата врага просто отражается в один кадр, из-за чего школьнику или начинающему игроку трудно подгадать момент для перепрыгивания. С паузой поведение описывается переменной направления `dir` (`-1` влево, `+1` вправо) и таймером ожидания `waitTimer`:

```text
если waitTimer > 0:
  waitTimer = waitTimer - dt
  Vx = 0
иначе:
  Vx = dir * speed
  X  = X + Vx * dt
  если X >= maxX:
    X         = maxX
    dir       = -1
    waitTimer = 0.5   // пауза 500 мс на правом краю
  если X <= minX:
    X         = minX
    dir       = +1
    waitTimer = 0.5   // пауза 500 мс на левом краю
```

**Конкретный расчёт цикла патруля:** пусть платформа имеет ширину `256` пикселей (8 клеток по `32`), враг ходит по отрезку от `minX = 224` до `maxX = 448` (длина пути `L = 224` пикселя) со скоростью `speed = 112 пикс/с` и останавливается на краях на `waitTime = 0,5 с`. Тогда:

```text
Время прохода в одну сторону = 224 / 112 = 2,0 с
Полный цикл патруля (туда + пауза + обратно + пауза) = 2,0 + 0,5 + 2,0 + 0,5 = 5,0 с
```

Каждые `2,5 секунды` враг замирает на полсекунды в предсказуемой точке, давая игроку чёткое окно для манёвра.

## Прощающий хитбокс: почему зону шипов уменьшают на 20–30%

Прощающий хитбокс (forgiving hitbox) — это намеренное несовпадение размера видимого спрайта и его физического прямоугольника столкновения в пользу игрока: опасные объекты делают физически меньше картинки, а полезные (монеты, аптечки, платформы) — чуть больше.

Когда на клетке `32×32` пикселя нарисованы треугольные шипы, верхняя треть клетки состоит из пустых пикселей между остриями. Если задать шипам полный квадратный хитбокс `32×32`, герой погибнет, задев пяткой пустой угол над шипом.

Правило расчёта усечённого прямоугольника опасности внутри клетки шириной `W` и высотой `H` со сжатием на `25%` по бокам и на `30%` сверху:

```text
hitboxW = W * 0.50              // для 32 пикс → 16 пикс (по 8 пикс отступа слева и справа)
hitboxH = H * 0.70              // для 32 пикс → 22 пикс (10 пикс запаса сверху у острия)
offsetX = (W - hitboxW) / 2     // +8 пикс от левого края спрайта
offsetY = H - hitboxH           // +10 пикс от верхнего края (прижат к основанию пола)
```

Таблица: Рекомендуемые размеры зон столкновения относительно видимого спрайта `32×32` пикселя

| Тип объекта на сцене | Размер видимого спрайта | Размер зоны столкновения (`W × H`) | Смещение относительно спрайта | Зачем нужен такой допуск |
|---|---|---|---|---|
| Хёртбокс игрока (получение урона) | `32 × 48` пикс | `22 × 40` пикс (`-31%` по ширине) | По центру, прижат к стопам | Касание врага краем плаща или волос не отнимает жизнь |
| Напольные шипы (опасность) | `32 × 32` пикс | `16 × 20` пикс (`-50%` ширины, `-37%` высоты) | Отступ `8` пикс с боков и `12` пикс сверху | Смерть наступает только при реальном наступании на остриё |
| Летящий снаряд врага | `16 × 16` пикс | `10 × 10` пикс (`-37%`) | По центру `(0,5; 0,5)` | Позволяет увернуться в плотном потоке снарядов |
| Монета или бонус (награда) | `16 × 16` пикс | `24 × 24` пикс (`+50%`) | По центру `(0,5; 0,5)` | Предмет подбирается, даже если герой пролетел в `4` пикселях рядом |

## Контраст и форма: как отличить опасность от декорации за долю секунды

В динамичной 2D-игре взгляд игрока сфокусирован на персонаже и точке в `100–150` пикселях впереди него, поэтому ловушки должны считываться периферическим зрением по трём визуальным признакам без чтения подписей:
1. **Язык геометрических форм:** скруглённые и прямоугольные силуэты мозг считывает как безопасные опоры (платформы, кнопки), а острые треугольные и диагональные зубцы — как угрозу (шипы, пилы, челюсти);
2. **Цветовой код сцены:** выберите один сигнальный цвет опасности на весь проект (например, ярко-алый или кислотно-фиолетовый) и **никогда** не используйте этот оттенок в безобидных фоновых цветах или декоративных кустах на слое параллакса;
3. **Разделение по слоям отрисовки:** все объекты, способные нанести урон или остановить движение, размещаются строго на основном игровом слое с коэффициентом параллакса `1,0` и чётким контуром.

## Практический сценарий: патрульный страж с предупреждением перед выпадом на JavaScript

Страж, который патрулирует отрезок `X = 224..448` с паузой `0,5 с` на краях, а при приближении героя ближе `96` пикселей (3 клетки) делает замах `0,35 с` и только потом бьёт, описывается конечным автоматом из четырёх состояний и одного таймера. Код ниже проверен в Node.js 24; комментарии показывают его фактический вывод.

```js
// Страж: патруль X = 224..448 со скоростью 112 пикс/с и паузой 0,5 с,
// при игроке ближе 96 пикс — замах 0,35 с, удар 0,15 с, восстановление 0,5 с
const MIN_X = 224, MAX_X = 448, SPEED = 112, WAIT = 0.5, SIGHT = 96;
const PHASES = { windup: 0.35, strike: 0.15, recovery: 0.5 };

function createGuard() { return { x: MIN_X, dir: 1, wait: 0, state: "patrol", timer: 0 }; }

function updateGuard(g, dt, playerX) {
  if (g.state === "patrol") {
    if (Math.abs(playerX - g.x) < SIGHT) { g.state = "windup"; g.timer = PHASES.windup; return; }
    if (g.wait > 0) { g.wait -= dt; return; }
    g.x += g.dir * SPEED * dt;
    if (g.x >= MAX_X) { g.x = MAX_X; g.dir = -1; g.wait = WAIT; }
    if (g.x <= MIN_X) { g.x = MIN_X; g.dir = 1; g.wait = WAIT; }
    return;
  }
  g.timer -= dt;
  if (g.timer > 0) return;
  if (g.state === "windup") { g.state = "strike"; g.timer = PHASES.strike; }
  else if (g.state === "strike") { g.state = "recovery"; g.timer = PHASES.recovery; }
  else { g.state = "patrol"; g.wait = WAIT; }
}
// Урон наносится только в фазе удара: касание во время замаха безопасно
function hurts(g, touching) { return touching && g.state === "strike"; }

// Проверка 1: полный цикл патруля без игрока поблизости
const dt = 1 / 60;
const g = createGuard();
let t = 0, turns = 0, lastDir = g.dir;
while (turns < 2) {
  updateGuard(g, dt, 10000); t += dt;
  if (g.dir !== lastDir) { turns++; lastDir = g.dir; }
}
while (g.wait > 0) { updateGuard(g, dt, 10000); t += dt; }
console.log("цикл патруля, с:", t.toFixed(2));          // 5.03 — 5 с с точностью до кадра 16,67 мс

// Проверка 2: сколько длится безопасный замах перед ударом
const h = createGuard();
let ms = 0;
updateGuard(h, dt, MIN_X + 50);                          // игрок подошёл на 50 пикс
while (h.state === "windup") { updateGuard(h, dt, MIN_X + 50); ms += dt * 1000; }
console.log("замах, мс:", Math.round(ms), "урон при касании в замахе:", hurts({ state: "windup" }, true)); // 367 false — 350 мс с точностью до кадра
```

Измеренные `5,03 с` и `367 мс` вместо `5,0 с` и `350 мс` — цена шага `1/60 с` (`16,67 мс`): таймер переключает состояние в первом кадре, где он стал не больше нуля, поэтому фаза никогда не бывает короче заданной. Зону поражения шипов в той же игре задавайте по формуле усечённого хитбокса из раздела выше, а не по размеру спрайта.

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

Описанная схема конечного автомата из нескольких состояний и прощающих прямоугольных зон имеет свои границы:

- **Стелс-игры и тактики со сложным поиском пути по лабиринту:** если `30` врагов должны обходить динамические преграды по сетке (алгоритм A*), слышать шаги сквозь двери и координировать обход с флангов, простого патруля между `minX` и `maxX` не хватит — потребуются граф навигации и поиск пути.
- **Файтинги киберспортивного уровня с проверкой попиксельных полигонов:** для турнирных дуэлей, где у каждого удара задаются отдельные прямоугольники головы, корпуса и ног на каждый кадр, одного уменьшенного хитбокса мало.
- **Мирные игры для самых маленьких:** если цель — исследование без поражения, фазы атаки и зоны урона лишние; достаточно безопасных препятствий и подсказок.

---

Мы разрабатываем **J2DRU** — браузерный 2D игровой движок; он в разработке и публично пока не открыт, заявку можно оставить на [странице раннего доступа](/early-access?utm_source=blog&utm_medium=article&utm_campaign=patrulirovanie-vragov-i-chitaemye-opasnosti) (подача заявки не гарантирует приглашение).

**Читайте также:**
- [Идеальный прыжок и секрет Coyote Time](/blog/idealnyj-pryzhok-i-sekret-coyote-time)
- [Игровой цикл и физика столкновений AABB](/blog/igrovoj-cikl-i-fizika-stolknovenij-aabb)
- [От первых аркад и «Змейки» до инди-хитов: история ключевых механик 2D-игр](/blog/istoriya-klyuchevyh-mehanik-2d-igr)
