Почему cssDOOM это не DOOM 😒
Выше обещал разобраться насколько проект
cssDoom близок к оригинальному doom. Короткий ответ: с точки зрения геймплея и реализация близка, а с точки зрения технической реализации рендеринга — нет.
Начну с того, что лично для себя я так и не понял, можно ли визуально похожие игры, но отличающиеся технически (со всеми вытекающими), считать одной и той же игрой? Вывод был бы однозначным, если бы не было такого понятия, как remastered. Это когда создатели берут старую игру и переводят ее на обновленный движок.
Интуитивно близкий подход к разработке Doom в браузере — это хранить где-то в памяти данные об уровне, затем на их основе вычислять геометрию уровня, текстуры, движение и прочее и прочее на стороне JavaScript, а с помощью Canvas API заниматься отрисовкой пикселей.
Автор проекта
cssDoom, Нильс Линхеер выбрал другой подход. Отрисовка происходит через DOM. DOOM в DOM, каламбур. В его подходе для сцены создается множество <div /> элементов, вместо отрисовки на canvas. Каждая стена, пол, бочка и монстр — это отдельный элемент в DOM-дереве. У каждого <div /> есть набор CSS-свойств, которые устанавливаются из настроек уровня, например:
<div class="wall" style="
--start-x: 2560;
--start-y: -2112;
--end-x: 2560;
--end-y: -2496;
--floor-z: 32;
--ceiling-z: 88;
">
Затем все дальнейшие расчеты берет на себя CSS. В репозитории проекта
прямо так и пишется, что на стороне CSS вычисляет ширину, высоту и 3D-преобразования с помощью тригонометрических функций:
.wall {
--delta-x: calc(var(--end-x) - var(--start-x));
--delta-y: calc(var(--end-y) - var(--start-y));
width: calc(hypot(var(--delta-x), var(--delta-y)) * 1px);
height: calc((var(--ceiling-z) - var(--floor-z)) * 1px);
transform:
translate3d(
calc(var(--start-x) * 1px),
calc(var(--ceiling-z) * -1px),
calc(var(--start-y) * -1px)
)
rotateY(atan2(var(--delta-y), var(--delta-x)));
}Здесь можно дальше погружаться в описание проекта и его исходный код, чтобы найти использование множества CSS свойств для отрисовки, и по итогу воскликнуть: А что так можно было что ли? Однако здесь я поднимаю другой вопрос.
Насколько это реализация повторяет оригинальный DOOMЕсли изучить код
WAD-файл и исходники, то можно заметить, что автор отказался от использования BSP-дерева (бинарного разбиения пространства). Напомню, что именно этот алгоритм, реализованный Джоном Кармаком, позволил оригинальному Doom эффективно работать на слабом железе 90-х: движок обходил только видимые стены и не тратил ресурсы на отрисовку перекрытых областей.
В cssDoom всё иначе. Вместо эффективного обхода стен и исключения ранее нарисованных при отрисовке следующих, автор рисует сразу все стены и предметы — используя CSS-свойство
translate3d.
Что это дает это свойство? ➡️ Графика перестает быть плоской 2D или условно трехмерной (2.5D), а становится полноценной 3D. То есть благодаря CSS-трансформациям каждый объект получает реальные координаты в пространстве. Кстати, в превью к заметке добавлена специально трехмерная версия игры, cssDoom сам такое позволяет делать.
➡️ Каждый элемент <div /> после попадает в отдельный композитный слой браузера в понятиях браузерного рендеринга. При изменении страницы происходит (пере)компоновка этих слоев — эту работу берет на себя GPU.
➡️ В оригинальном Doom всё рисовалось исключительно на процессоре, пиксель за пикселем, с кучей ручных оптимизаций. В cssDoom мы уходим от идеи «нарисовать для уровня 1 пиксель 1 раз» — вместо этого браузерный движок сам решает, как и когда обновлять слои через видеокарту.
Что же это тогдаРазработчики прошлого старались искать всевозможные оптимизации, чтобы добиться максимальной скорости отрисовки на слабых машинах. Проект cssDoom это скорее концепт или технический эксперимент, способный визуально повторить и показать возможности современного CSS, но безусловно достойный внимания и уважения 🎩