Содержание
ASCII-город в стиле киберпанк получил играбельную браузерную сборку: соло-разработчик Grow Now! Games запустил 3D-демо на движке Rust WebAssembly размером 283 КБ. Мир рисуется не моделями и текстурами, а сеткой символов, которую WebGL выводит как обычную 3D-сцену.
Сборка вышла на этой неделе вместе со вторым роликом ASCII City Update: Interiors, Elevation & Skyscrapers. В нем разработчик показал интерьеры, перепады высоты и небоскребы. Первая демонстрация уже собрала больше миллиона просмотров, так что проект явно попал в нерв любителям странных движков.
Вся 3D-сцена собрана из символов ASCII
Демо запускается в браузере и выглядит как прогулка по киберпанковому району, но под капотом нет привычного набора ассетов из современных движков. Rust генерирует город, JavaScript получает упакованные данные, а WebGL превращает символы в кадр.
Tom’s Hardware разобрал браузерную сборку и нашел файл ascii-city-engine_bg-DqjAhqbp.wasm. Модуль занимает 283 КБ, при старте резервирует 1 152 КБ линейной памяти и сообщает версию движка 0.1.0 при запуске вне браузера.
- initialise_native_game: инициализация нативной игровой логики
- step_native_game: один шаг симуляции мира
- generate_native_local_map: генерация локальной карты
В бинарнике остались debug-пути к Rust-модулям. Среди них city.rs, world.rs, population.rs, interiors.rs и rendering.rs. Это хороший срез структуры: город, мир, население, интерьеры и отрисовка живут отдельными частями.
Rust отдает JavaScript base64-упакованные массивы байтов. В них лежат высоты зданий, типы тайлов, поверхности, оттенки, насыщенность, стили окон, флаги подсветки и ID планировок этажей.
WebGL рисует символы как текстурированные квадраты
Графический слой устроен прагматично: рендерер запрашивает WebGL-контекст и компилирует пару шейдеров. Фрагментный шейдер делает один texture2D-запрос, без сложного освещения и постобработки.
Символы сначала рисуются в atlas canvas через fillText. Движок кэширует каждую комбинацию символа и цвета, затем отправляет атлас на GPU одним texImage2D. После этого каждый знак превращается в текстурированный quad из шести вершин.
Размер ячейки атласа — 11 пикселей по высоте. Ширина равна monospace advance плюс два пикселя. За кадр все видимые ячейки уходят в один buffer upload, затем WebGL вызывает один drawArrays.
На десктопе сцена рендерится сеткой 180 на 80. Это 14 400 символьных ячеек в 10px Consolas, с девятью пикселями на строку. На сенсорных устройствах движок снижает сетку до 48 строк и 96-168 колонок.
Для мобильного профиля Rust использует отдельный режим 160 на 112. Vertex buffer заранее выделяет 2,76 МБ и удваивается, если кадр не помещается в текущий объем.
Без Unity и Unreal, но не без инженерной хитрости
Grow Now! Games раньше описывал исходную демку PC Gamer так: “no Unity, there’s no Unreal, there’s no 3D models, textures, or shaders”. Формулировка передает идею проекта: автор не собирал город из стандартных 3D-ассетов и готового игрового движка.
Технический разбор текущей браузерной версии показывает нюанс. Для вывода на экран здесь все же работают два WebGL-шейдера. Но игровой мир остается текстовым: здания, окна и поверхности кодируются символами, а не мешами и PBR-материалами.
Если WebGL недоступен, сборка переключается на Canvas 2D и рисует через drawImage. Есть и прямой путь через per-cell fillText, который включается параметром ?direct в URL. Это уже запасной режим, а не основной сценарий.
Такие проекты цепляют не графикой ради графики, а инженерной экономией. В похожем поле уже были Backrooms для Sega 32X на самописном raycasting-движке на C и SH-2 assembly, Doom-трибьют на SQL views с примерно 30 FPS при 128 на 64 и ASCII-рогалик Thunder Lizard, который нейросети перерисовывали в движение примерно на 10 FPS.
Браузерная сборка ASCII City уже доступна для запуска. При инстанцировании вне браузера движок сообщает версию 0.1.0, а его WebAssembly-модуль занимает 283 КБ.