DOOM-платформер на чистом JavaScript: даем вторую жизнь старой курсовой
Предыстория: как один проект разделился надвое
Несколько лет назад перед нами с одногруппником встала задача сдать зачёт по C++. Вместо банальных калькуляторов или генераторов паролей мы решили замахнуться на полноценную игру — как раз вовремя подвернулась новость о 30-летнем юбилее легендарной DOOM. Опыта командной разработки у нас не было, поэтому распределение ролей вышло простым: товарищ взял на себя написание псевдо-3D-движка на классическом рейкастинге, а я занялся 2D-платформером. Замысел был эффектным: платформер планировался как мини-игра на аркадном автомате прямо внутри трёхмерного мира, где собранные боеприпасы переносились бы в основную игру.
Проблема возникла не на уровне архитектуры или геймдизайна, а на базовом этапе, который мы благополучно проглядели: мы не согласовали технологический стек. По совету преподавателя я выбрал SFML — библиотека была мне знакома по лабораторным, она предлагает удобную работу со спрайтами, окнами и звуком сразу из коробки. Одногруппник же остановился на SDL, вдохновившись туториалом по рейкастингу: прямой доступ к буферу пикселей позволял рисовать вертикальные полосы стен без лишних слоев абстракции.
Каждое решение по отдельности было полностью оправданным, но вместе они не стыковались. Объединить SDL и SFML в одном приложении технически возможно, но согласование двух независимых циклов событий (event loop), различных подходов к окнам и таймингу превратило бы зачётную работу в полноценную курсовую, на которую оставалось буквально два-три дня. Мы трезво оценили ресурсы и защитили проект как две автономные программы. Преподаватель оценил задумку, резюмировав: «Идея отличная, жаль, что не свели воедино». На тот момент главное было получить зачёт, и мы успокоились.
С тех пор платформер почти три года пылился в архиве: исходник на 900 строк с зашитой прямо в код ASCII-картой и пачка DLL от SFML. Недавно я наткнулся на старый репозиторий напарника и подумал: раз объединять движки на C++ уже неактуально, почему бы не портировать свою часть на чистый JavaScript и Canvas — без сторонних фреймворков и библиотек, как я обычно и практикую. Заодно было интересно взглянуть на старые костыли и баги, которые мы так старательно маскировали на защите.

Ретроспектива: DOOM 1993 года и студенческий рейкастинг
Релиз DOOM от студии id Software состоялся 10 декабря 1993 года. В крошечной команде Джон Кармак отвечал за архитектуру движка, Джон Ромеро проектировал уровни, Эдриан Кармак рисовал графику, а Бобби Принс писал саундтрек. После успеха Wolfenstein 3D новая игра должна была доказать, что персональные компьютеры того времени способны отображать реалистичное трехмерное окружение в реальном времени.
Стоит оговориться: фраза «мы делали свой DOOM» — лишь красивое обобщение. То, что реально написать за семестр силами двух студентов, функционально куда ближе к Wolfenstein 3D, а пропасть между ними как раз и составляет главное технологическое достижение id Software.

Wolfenstein 3D генерирует картинку с помощью рейкастинга: из каждого столбца экрана выпускается луч по регулярной ортогональной сетке до ближайшей стены, а расстояние определяет высоту экранной полосы. Метод гениален своей простотой и экономичностью, но имеет жесткие рамки: стены строго под 90 градусов, фиксированная высота уровня, никаких лестниц и многоярусных пространств.
В DOOM эти ограничения обошли принципиально иной структурой — BSP-деревьями (Binary Space Partitioning). Локация еще на этапе компиляции карты разбивается на выпуклые секторы, поэтому движок отрисовывает полигоны в строгом порядке от ближних к дальним без покадровой сортировки. Это позволило строить стены под произвольными углами, варьировать высоту потолков и полов в смежных секторах и создавать убедительную иллюзию сложной архитектуры, хотя полы оставались плоскими, а настоящая многоэтажность появилась лишь к Doom 3.
Наш студенческий рейкастинг был скромнее, но все же интереснее, чем мне запомнилось. Заглянув в репозиторий товарища, я нашел описание реализованной механики: два отдельных бинарника — 2D-часть на SFML и псевдо-3D на SDL — связывались через общий файл. Платформер записывал количество собранных патронов в `bullets.txt`, а 2.5D-игра считывала его при старте и умножала запас на количество сохраненных жизней. Это был рабочий обмен данными на уровне файловой системы вместо монолитного приложения.
Собрать SDL и SFML в едином коде это не помогло бы — проблемы с event loop и таймингами оставались, — но стало ясно, что идея интеграции была реализована на практике, пусть и через промежуточный файл.
Часть 1. Анатомия старого кода
В исходнике Source.cpp содержалась матрица символов 67 на 150, описывающая тайловую структуру карты.

Символ «z» кодировал стену, «k» — платформу, «b/B» — монеты, «c» — интерактивный блок с сердечком, а «p» и «9» — порталы между секциями. Проверка коллизий шла банальным перебором тайлов под прямоугольником героя — наивный, но безотказный способ, который я без изменений перенес в JS. Сама сеточная концепция ближе к структуре Wolfenstein 3D, где уровень строго привязан к матрице блоков. В DOOM от этого отказались: карты строились вручную из произвольных полигонов в редакторе DoomEd.
Спрайты считывались из общего атласа hero1.png через структуру `IntRect`. В SFML отрицательная ширина автоматически зеркалит изображение без дополнительного кода. В Canvas такой прием не работает (отрицательные размеры в drawImage вызывают ошибку), поэтому горизонтальное отражение пришлось реализовывать программно. Примечательно, что в оригинальном DOOM для экономии дискового пространства спрайты врагов также хранились лишь для части ракурсов, а недостающие углы зеркалились движком на лету.
Часть 2. Поведение и характер врагов
В старой версии противники вели себя примитивно: двигались по прямой и разворачивались при контакте со стеной. В DOOM для каждого монстра применялся полноценный конечный автомат (состояния патрулирования, преследования, атаки, получения урона и гибели): Lost Soul стремительно таранил игрока, а Cacodemon парил в воздухе и стрелял снарядами. Для JS-порта я ускорил мобов на 50%, добавил им полноценную гравитацию и прописал дифференцированные модели поведения.
Летающий враг теперь отслеживает позицию персонажа: находясь на земле, он совершает периодические направленные прыжки в сторону игрока с заданным кулдауном, что выглядит как осознанное нападение.
Крупный тип врага при обнаружении героя выдерживает короткую паузу и совершает стремительный рывок на учетверенной скорости, имитируя поведение тарана или того же Lost Soul.
Часть 3. Процедурная генерация вместо статичной карты
Переписывая проект, я решил отказаться от жестко закодированной матрицы символов. Здесь я пошел вразрез с подходом id Software: в оригинальном DOOM уровни создавались исключительно вручную, я же написал процедурный генератор. Перепады высот грунта ограничены одним тайлом, что исключает появление непроходимых участков, а платформы с наградами генерируются на безопасном расстоянии друг от друга, не перегружая верхнюю часть уровня.
Итог

От старой кодовой базы на C++/SFML в новой реализации не осталось ничего, однако исходная логика, тайловая сетка и концепция мини-игры сохранены полностью — теперь без зависимостей от внешних DLL и платформенных ограничений.
За прошедшие десятилетия DOOM запускали на всем подряд — от принтеров и терминалов оплаты до тестов на беременность. Мой перенос студенческого кода на чистый Canvas — дань той же инженерной романтике: вернуть к жизни старый проект на новой платформе. Возможно, позже я портирую и рейкастинг-движок товарища, окончательно закрыв тот студенческий гештальт.
Главные выводы
Анализировать свой старый код бывает неловко: в нем не хватает структуры, оптимизации и изящных решений. Тем не менее этот опыт стал отличной точкой отсчета, а переработанная версия наглядно показала, как меняются подходы к разработке со временем.
Опробовать веб-версию можно здесь.
P.S. Если у вас есть предложения по оптимизации механик или вы обнаружили баг — делитесь своими мыслями в комментариях.
© 2026 ООО «МТ ФИНАНС»
Для Alan Wake 2 вышло крупное обновление спустя почти три года после релиза
Capcom поделилась новыми подробностями Onimusha: Way of the Sword о противостоянии Они и демонов Гэнма
Дэвид Харбор привлек внимание фанатов одной репликой из фильма «Мстители: Доктор Дум»
Sony готовит новую версию геймпада DualSense Edge с неожиданным изменением
Дэвид Харбор предложил Кристоферу Нолану снять экранизацию World of Warcraft
Tides of Annihilation оказалась не просто красивой игрой — первые впечатления IGN
Konami опубликовала 19 минут геймплея Silent Hill: Townfall
Предзаказ виниловых фигурок левиафанов из Subnautica от YouTooz откроется 21 августа