javascript

Пишем Pac-Man на чистом JavaScript: ностальгия, «Пиксели» и туман войны

  • четверг, 19 марта 2026 г. в 00:00:05
https://habr.com/ru/companies/ruvds/articles/1009948/
Сцена почти как в фильме
Сцена почти как в фильме

1. Введение: кино, которое разбудило воспоминания.

Недавно, когда писал статью про «Тетрис», я поймал себя на мысли, что вспоминаю не только падающие блоки, но и другой культовый проект — жёлтый кругляш, вечно убегающий от призраков. А потом в памяти всплыл фильм «Пиксели» (2015). Там есть сцена, которая одновременно смешная — жутковатая: создатель Пакмана, Тору Иватани, выходит к гигантскому Пакмену и пытается с ним поговорить по-отечески: 

«Пакман, я сделал тебя. Ты такой голодный, я знаю. Не надо больше есть. Хороший мальчик» — актёр Денис Акияма (Тору Иватани).

А Пакман в ответ... откусывает ему руку.

И вот я подумал: а почему бы не попробовать самому создать Пакмена? Не такого кровожадного, конечно, а просто для души. Тем более что с «Тетрисом» получилось удачно, захотелось закрепить успех.

Если вы не смотрели «Пиксели» — ничего страшного. Это лёгкая комедия с понятным сюжетом: шутки, спецэффекты и тоска по играм, на которых многие выросли. Но меня зацепило другое, когда смотришь этот фильм, ловишь себя на мысли, что старые игры — они как часть жизни.

Пакмен, пришельцы из Space Invaders, герои Donkey Kong — они никуда не уходят. Даже когда их персонажи исчезают с экранов, остаётся память. О том детстве, когда счастье измерялось количеством набранных очков. Хотя, если честно, с тех пор мало что изменилось — просто цели стали другими.

А теперь немного про сам проект и почему я решил не просто скопировать классику, а добавить что-то своё.


2. История создания: Как жёлтый кругляш покорил мир

примерно так я это представляю
примерно так я это представляю

Всё началось в 1980 году. Японец Тору Иватани, которому тогда было 25 лет, создал игру, ставшую символом целой эпохи. Говорят, идея пришла к нему за обедом — когда он откусил кусок пиццы и увидел в оставшейся части знакомый образ. Так появился Пакмен — персонаж, который должен был привлечь в игровые залы не только подростков, но и девушек.

Кстати, про название. Сначала игру хотели назвать Puck Man — от японского «паку-паку» (звук, с которым открывается и закрывается рот). Но в Америке быстро смекнули: букву «P» в слове Puck легко подрисовать до неприличного слова на букву «F». Чтобы игровые автоматы не портили вандалы, название сменили на Pac-Man. Просто и прагматично.

Видите ли, разработка игр часто начинается со слов. Я начал играть со словом, делая наброски в своём блокноте. Все доступные в то время компьютерные игры были жестокими — военные игры и игры типа «Космический захватчик». Не было игр, которые могли бы понравиться всем, и особенно не было игр для женщин. Я хотел придумать «комическую» игру, которая бы понравилась женщинам.

История, которую я люблю рассказывать о происхождении Пакмана, такова: однажды в обеденное время я был очень голоден и заказал целую пиццу. Я взял себе кусочек, и то, что осталось, стало идеей для формы Пакмана, — вспоминал позже Иватани (источник).

И это сработало. Пакмен стал не просто игрой, а культурным феноменом. По мотивам выпускали мультсериалы, игрушки, одежду. А в 2010 году Google выпустил первый в истории интерактивный дудл к 30-летию игры.


3. Мой проект: Pac-Man с туманом войны

Вдохновившись этой историей, я решил создать свою версию. Но просто скопировать классику — скучно. Нужно было придумать фишку, которая отличала бы мою игру от тысяч других клонов. И тут я вспомнил про туман войны из стратегий — почему бы не добавить его в Пакмена?

3.1. Цели проекта

Главная задача — не создать идеальный код и не утверждать, что это нечто революционное. Всё проще: написать игру для души, вспомнить старые времена и поделиться результатом. А заодно спросить совета у сообщества — как лучше сделать призраков и что считать победой.

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

Весь код открыт и находится на GitHub (это не финальная часть проекта, будет вторая часть, где будет много новых функций). Пока можно просто смотреть, форкать и предлагать улучшения.

3.2. Технические детали

Как и в проекте с «Тетрисом», здесь ничего сверхсложного нет. Всё на том, что уже есть в любом браузере.

Что использовал:

  • HTML — чтобы разложить поле для игры, джойстик, счёт.

  • CSS — анимации, повороты спрайтов, све��ение.

  • JavaScript — ну куда без него, вся логика на нём.

Структура проекта:

├── index.html               # Сама страница с игрой
├── style.css                # Дизайн и анимации
├── game-constants.js        # Константы: размеры карты, скорость, режимы
├── game.js                  # Основная логика Пакмена
├── ghosts.js                # Поведение призраков
└── controls.js              # Управление (клавиши + джойстик)

3.3. Игровое поле — это матрица.

Как и в «Тетрисе», всё построено на двумерном массиве. Карта — это массив строк, где каждый символ отвечает за тип клетки:

const MAZE = [
  "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "X...o..X................o......X",
  "XX.XX.XXX.XXX.XXX.XXXXXX.XXXX.XX",
  // и так далее...
];

Расшифровка простая:

  • X — стена.

  • . — точка (10 очков).

  • o — энерджайзер (50 очков + режим испуга для призраков).

  • BPIC — домики призраков.

Размер клетки — 15 на 15 пикселей. Это позволяет легко рассчитывать позиции: просто умножаем номер колонки или строки на 15.

3.4. Проверка столкновений

Как и в «Тетрисе», прежде чем подвинуть Пакмена или призрака, нужно проверить — а можно ли? Не врежется ли он в стену?

Для этого есть простая функция, которая смотрит, что находится в соседней клетке:

function canMoveInDirection(dir, row, col) {
    if (dir === 0) return col + 1 < MAZE_COLS && MAZE[row][col + 1] !== 'X';
    if (dir === 1) return col - 1 >= 0 && MAZE[row][col - 1] !== 'X';
    if (dir === 2) return row - 1 >= 0 && MAZE[row - 1][col] !== 'X';
    if (dir === 3) return row + 1 < MAZE_ROWS && MAZE[row + 1][col] !== 'X';
    return false;
}

На этой простой проверке держится всё движение. Хочешь идти вправо? Проверь, что справа не стена. Всё честно.

3.5. Туман войны — главная фишка

А вот здесь самое интересное. В классическом Пакмене видно весь лабиринт. В моей версии — только то, что вокруг Пакмена в радиусе 6 клеток. Всё остальное скрыто полупрозрачным туманом.

Работает это так: после каждого движения мы проходим по всем стенам, точкам и энерджайзерам, считаем расстояние до Пакмена и меняем прозрачность:

function updateVisibility() {
    const allCells = [walls, dots, energizers];
    
    for (let t = 0; t < allCells.length; t++) {
        for (let i = 0; i < allCells[t].length; i++) {
            const cell = allCells[t][i];
            const row = parseInt(cell.dataset.row);
            const col = parseInt(cell.dataset.col);
            const distance = Math.max(Math.abs(row - gridPosition.row), 
                                      Math.abs(col - gridPosition.col));
            
            if (distance <= VISIBLE_RADIUS) {
                cell.style.opacity = '1';
                cell.style.filter = 'none';
            } else {
                const opacity = Math.max(0.1, 1 - (distance - VISIBLE_RADIUS) * 0.2);
                cell.style.opacity = opacity;
                cell.style.filter = 'brightness(0.5)';
            }
        }
    }
}

Формула подобрана так, что на границе видимости клетки ещё различимы, а дальше плавно исчезают. Создаёт эффект неизвестности — а вдруг там призрак?

3.6. Анимация через requestAnimationFrame

Движение сделано через requestAnimationFrame — это стандартный способ для анимаций в браузере.

position.x += MOVE_SPEED * deltaTime;
if (position.x >= targetX) {
    position.x = targetX;
    // дошли до цели
}
pacman.style.left = position.x + 'px';

А направление поворота задаётся через data-атрибут, а CSS уже доворачивает спрайт:

[data-direction="0"] { transform: scaleX(1) rotate(0deg); }  // вправо
[data-direction="1"] { transform: scaleX(-1) rotate(0deg); } // влево
[data-direction="2"] { transform: rotate(-90deg); }          // вверх
[data-direction="3"] { transform: rotate(90deg); }           // вниз

3.7. Призраки — отдельная песня

Это самая сложная часть. В оригинале у каждого призрака своя логика поведения. Я попытался её воспроизвести:

  • Blinky (красный) — просто преследует Пакмена.

  • Pinky (розовый) — целится на 4 клетки вперёд по направлению движения.

  • Inky (голубой) — использует позицию Blinky и направление Пакмена для сложных манёвров.

  • Clyde (оранжевый) — если далеко бежит к Пакмену, если близко убегает в свой угол.

Плюс есть режимы SCATTER (разбегание по углам) и CHASE (преследование), которые чередуются по таймеру:

const MODE_TIMINGS = [
  { mode: 'scatter', duration: 7 },
  { mode: 'chase', duration: 20 },
  { mode: 'scatter', duration: 7 },
  { mode: 'chase', duration: 20 },
  { mode: 'scatter', duration: 5 },
  { mode: 'chase', duration: 20 },
  { mode: 'scatter', duration: 5 },
  { mode: 'chase', duration: Infinity } 
];

И когда Пакмен съедает энерджайзер, призраки пугаются, синеют и начинают убегать — тогда их можно съесть за дополнительные очки.

3.8. Управление — клавиши и джойстик.

Сделал два варианта: для компьютера (клавиши WASD и стрелки) и для телефонов (виртуальный джойстик). Маппинг простой:

const keyMap = {
  'w': 2, 's': 3, 'a': 1, 'd': 0,
  'arrowup': 2, 'arrowdown': 3, 'arrowleft': 1, 'arrowright': 0
};

Джойстик — это крестовина, при нажатии на кнопки меняется положение центрального кружка и направление Пакмена.


4. Итоги:

4.1. Что получилось в итоге.

итог
итог

В результате разработки я сделал версию Pac-Man — проект, в который можно играть прямо в браузере.

Игра доступна онлайн — вы можете испытать её сами.

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

4.2. Что дальше и вопросы к сообществу

Проект пока в разработке, и я хочу сделать его лучше. Исходный код опубликован на GitHub — забирайте, экспериментируйте, делайте свои версии.

Сейчас проект уже приобрёл очертания, однако много чего ещё нет, например: условия победы или хотя бы появления точек заново для бесконечного режима: лабиринт, точки, призраки, туман. Механика работает, Пакмен бегает, призраки пугаются. Но я думаю, что после пятого уровня становится немного грустно — а что дальше? Бесконечно собирать точки, пока не поймают? В оригинале это работало из-за того, что уровни просто повторялись с повышением скорости. Но хочется чего-то большего.

Может, сделать так, чтобы с каждым уровнем лабиринт менялся сам? Или добавлять новые типы врагов — например, призраков, которые не боятся энерджайзеров?

Идей пока не хватает, поэтому буду рад, если подкинете что-то в комментариях.

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

© 2026 ООО «МТ ФИНАНС»