Как я сделал ремейк Disciples 2 (Gallean)
Всем привет! Я новорег и фанат Disciples 2. Это перевод блогпоста Remaking Disciples 2 (Gallean).
В оригинальной версии на сайте много интерактива и видео, например, с послойной разверткой того, как рендерится битва и как были переделаны юниты. Я как смог адаптировал их для статьи, но в оригинале все равно выглядит лучше. В переводе я оставил ссылки на интерактивные штуковины.
Привет, собратья по серии Disciples!
Хочу представить вам Gallean, кроссплатформенный ремейк Disciples 2 с открытым кодом, 4K артом, поддержкой всех кампаний и сценариев и полной локализацией. В нем я реализовал все известные мне механики оригинальной игры. А еще я научил компьютерных оппонентов играть с честным туманом войны и так, что иногда прямо реально приходится терпеть.
Кстати, почему именно Gallean?
По лору, Галлеан женился на самой Смерти, был убит богом гномов и бросил своих детей. В общем, приемлемое имя для игры.
Gallean 0.8 это первый публичный релиз (прим: сейчас уже доступна версия 0.12 с локализациями). Я решил не выпускать с колес версию 1.0, потому что планирую еще докрутить мультиплеер, а для этого может потребоваться изменить формат сейвов¹. Если кто-то вдруг планирует поиграть с друзьями, то есть hotseat.
Это первый игровой проект, который я сделал от начала и до конца, и, мягко говоря, местами было совсем непонятно, как быть. В какой-то момент я решил делать его скорее как корпоративное приложение, а не игру. Немного ортодоксально, но это помогло мне добраться до релиза, не поехав головой.
Оглядываясь на результат, я доволен тем, что у меня получился супер-простой .NET пайплайн через dotnet build/publish, который компилирует все в нативный код, а еще тем, что я написал кучу тестов на основной движок.
Что пока не супер, так это то, что ИИ регулярно творит всякую дичь на глобальной карте. Например, он легко может на ходе 2 проиграть сценарий на 80+ ходов, скормив Дрега Зул кракену.
Хотя иногда у него бывают и просветления: сидишь ты себе, раскачиваешься потихоньку, а тут на тебя выезжает идеально заминмаксенный стек с тремя Берсеркерами.
Скачать ремейк можно на https://gallean.org/download, а исходный код есть на GitHub.
Чтобы запустить игру, нужно иметь набор ассетов, который можно получить одним из двух способов: либо найти существующий .galleanpack, либо самому прогнать оригинальные файлы игры через пайплайн для ассетов. При первом запуске игры будет пошаговая инструкция.
Дальше будет немного ностальгических воспоминаний. Их можно пропустить и перейти сразу к содержательному контенту.
Как я до этого докатился
Однажды в детстве, в совершенно обычный скучный день, отец принес домой диск. У него была какая-то странная хрустящая упаковка. Я даже подумал, что это была такая бумага, в которую обычно заворачивают цветы.
У нас был Celeron 266, который тоже хрустел примерно при любом действии. Поэтому установка игры сопровождалась шуршанием обертки, потрескиванием жесткого диска и совершенно непривычными сейчас звуками раскручивающегося CD привода. Но это все была ерунда…
…по сравнению с тем, что случилось потом. Раскат грома и меланхоличная нарастающая мелодия из главного меню Disciples 2.
От этого вступления до сих пор иногда пробегает холодок по спине.
Я хорошо помню, как смотрел на игру из-за плеча и помню, как играл сам. Мне надолго запомнилась первая миссия за Империю (кто проходил, наверно вспомнит этот момент). В самом начале игры, буквально через пару минут внезапно во время твоего хода выбегает огромный орк с криком "ХОААР!" и говорит, что поймал героя в ловушку. Мне тогда хватило впечатлений, чтобы не запускать игру несколько дней.
У Disciples 2 была совершенно особенная эстетика, потрясающая музыка и мрачная кампания, в которой есть над чем поразмышлять. Двадцать пять лет спустя, уже при восстановлении оригинальных кампаний, все эти размышления накатили на меня с новой силой.
Возможно, это прозвучит наивно, но все эти годы я ждал ремейк. Мне кажется, что у серии для этого есть все, что нужно: преданные поклонники, ни на что не похожая атмосфера, немного устаревший, но все еще увлекательный геймплей. На худой конец, можно было бы хотя бы сделать игру для мобилок².
Решение сделать ремейк
И как-то так получилось, что я решил сделать ремейк сам. К счастью, у меня за плечами был довольно богатый опыт:
- Три с половиной класса художки. Я вылетел из нее после того, как на экзамене по истории искусств не смог отличить Босха от современной живописи. Еще все учителя говорили мне, что они никогда не видели, чтобы акварелью писали так грязно.
- Кое-какие знания о разработке ПО. То есть, как примерно писать софт так, чтобы он не разваливался на ходу.
- Мои университетские познания в 3D-графике. Помнится, тогда я написал собственный движок, который использовал теперь уже бородатые (а тогда новейшие!) шейдеры Nvidia CG. Он умел рисовать водичку с кривыми каустиками, а еще брал геометрические шейдеры и генерил с их помощью ландшафт с дырками.
С этим багажом я сразу же решил, что нужно сделать так, чтобы ремейк не требовал от меня рисовать юнитов руками.
Забегая вперед, я наверно хотел бы побольше рассказать про архитектуру движка и про игру вообще. Но лучше вместо этого начну с части, которую все должны помнить очень хорошо: со сражений.
Новое поле боя
Поле боя — визитная карточка Disciples 2. На нем время проходит незаметно за продумыванием ходов, экспериментами с разными составами отряда или просто перелопачиванием врагов. Как и во многих других играх той эпохи, оно почти ничего не прощает: один промах легко может загубить битву на 15 ходов, а без показа очередности хода тебя то и дело застают врасплох сменой порядка.
Чтобы вы не заскучали перед технической частью, сначала покажу, как теперь рендерится боевая сцена:
На сайте она разобрана по слоям (фон, карты глубин и нормалей, эффекты, источники света, юниты, UI). Автор использует подход примерно как в Pillars of Eternity с пререндерингом всего в карты нормалей/глубины/альбедо и освещает все динамически.
С чего я начал
В Disciples 2 не то чтобы прямо много исходных данных, от которых можно оттолкнуться для ремейка.
Оригинальная версия рендерится с разрешением 800×600 и 16-битной цветовой палитрой, используя анимированные спрайты с тремя отдельными позами: idle, attack, hit. Анимации можно состыковывать: позы attack и hit начинаются из idle и возвращаются обратно в idle.
В спрайтах отдельно закодированы слои для самих юнитов, их теней и аур (прим: ауры в этой терминологии это практически любое свечение и все эффекты, которые накладываются на атаки). При рендеринге все слои отрисовываются каждый на своей глубине, чтобы не перекрывать друг друга.
Все спрайты были сделаны из 3D-моделей с неплохим для своего времени бюджетом полигонов, но не все из них прошли проверку временем. Это особенно заметно на небольших юнитах, по которым видно, что художники скипали детализацию, потому что детали в итоге превратятся в пару-тройку пикселей на экране.
Все юниты используют фиксированный источник света, расположенный слегка над ними. А фоны специально сделаны с хитрой изометрической проекцией, чтобы визуально компенсировать одинаковые размеры юнитов и отсутствие полноценной глубины.
Посмотрев на это, я решил зафиксировать, что я хотел бы от ремейка в визуальном плане. Иначе возникает проблема: если все в принципе можно подрезать, то очень легко сбиться с пути.
Я решил, что ремейк должен:
- Хорошо выглядеть и работать в 4K. Еще поддерживать разные соотношения сторон вплоть до 21:9 и не тормозить в 4K даже на встроенной графике. Оригиналу хватало 64 МБ оперативной памяти и железа начала двухтысячных, поэтому как-то стыдно было бы замахнуться на меньшее.
- Динамически освещать все поле боя. В смысле, юнитов и их окружение, с правильной реакцией на световые эффекты. Я решил не пытаться восстанавливать настоящие трехмерные модели (сложно!), так что для освещения мне предстояло восстановить информацию о нормалях и, возможно, глубине прямо из пиксельных спрайтов.
- Полностью переработать фоны. То есть, сделать новые с нуля, а не реставрировать старые. Возможно, даже с полноценной 3D-геометрией.
- Дать игрокам возможность самим прогнать пайплайн для ассетов. Я решил назвать его Umbra. Пайплайн должен был стать полноценной частью проекта на том же .NET, с детерминированным результатом и возможностью запустить его на обычном железе³.
Увеличиваем юнитов
По пиксельным юнитам более-менее понятно, что допрыгнуть сразу до 4K не получится и нужно будет несколько этапов. Поэтому я начал с относительно простой задачи: увеличить юнитов в два раза. Но у меня была небольшая стратегия, которой я собирался придерживаться. Идея в том, что чем больше оригинальной информации удастся восстановить на первых этапах процессинга, тем лучше будет конечный результат.
Как выяснилось, у этого подхода есть неприятное обратное свойство: если растерять информацию в начале восстановления, то как ни старайся потом, получится мусорка. У меня примерно так и вышло, когда я начал с 2× апскейлинга, отправив все анимации в ESRGAN⁴:
Результат получился плачевный. Оказывается, для анимации недостаточно сделать каждый кадр по отдельности хорошим, потому что тогда анимации размазываются и в них начинают скакать детали. Ну и еще конечно не надо использовать апскейлер, обученный на статичных картинках из аниме.
После некоторого количества экспериментов до меня дошло, что, хотя технически спрайты — это отдельные картинки с прозрачностью, обрабатывать их нужно цельно и как ВИДЕО. При такой обработке отдельные кадры перестают отличаться в деталях. И на самом деле в таком случае собирается намного больше исходной информации: между кадрами на самом деле спрятано много данных, которых не видно, если смотреть на кадры в отдельности. А модели для восстановления видео умеют этим пользоваться. В итоге для первого шага я выбрал BasicVSR++.
Про детали: представьте себе три соседних кадра, и нужная нам деталь хорошо видна только на одном из них. Модель может взять ее из этого кадра, запихать это в контекст и тогда детали из этого кадра будут использованы на всей последовательности.
Почему три? Ну я попробовал три и вроде норм.
Правда, после такого процессинга деталей все еще не хватало для 4K. Пришлось выкручиваться дальше.
Увеличиваем детализацию
Даже нормально увеличенный вдвое спрайт для небольшого юнита получается где-то 200×200 пикселей. Мало.
Я поломал голову, но не придумал ничего лучше, кроме как добавить этап генеративной доработки. Но правда я решил ограничить его так, чтобы придумки происходили только в тех местах, где у модели низкая уверенность в исходных деталях. По сути, на небольших юнитах. Я выбрал относительно свежую модель LTX и скармливал ей промпты, которые я кодировал через Gemma4. Это все сделано прямо в пайплайне.
Дальше я потратил прилично времени на арендованных GPU, перебирая лица почти всех юнитов. В Disciples 2 портреты это часть спрайта со стилизацией и обработкой, но при этом они не всегда совпадают. Поэтому пришлось все внимательно рассматривать на всех анимациях:
За это время я нашел небольшой полезный прием: можно нарисовать heatmap с информацией о том, куда пришлись токены из промпта. Это помогает понять, какая часть промпта работает, а какая вообще игнорируется:
Дальше у меня возникла еще одна проблема: LTX работает с RGB видео и ВООБЩЕ ничего не знает про прозрачность. Просто приклеить обратно альфу от оригинального спрайта нельзя, потому что детали уже другие и их края не совпадают. Дурацкая ситуация, потому что мы просто выкинули информацию о прозрачности, а теперь должны как-то восстановить ее.
Сначала я попробовал прогонять один и тот же исходник два раза на двух разных фонах, а потом соединять результат. Идея в том, что типа пиксели, которые получились одинаковыми и есть сам юнит, а все разное это фон.
Но такое не работает. Потому что если изменить фон, то меняется все состояние модели даже с абсолютно идентичным входным шумом. И она не может выдать одинаковые результаты. А если нет одинаковых результатов, то нет и общих пикселей.
Я не придумал ничего лучше, кроме того, как до перехода к LTX добавить синий или зеленый хромакей и потом убрать его еще одной моделью, CorridorKey. Работает примерно как при съемке в студии на зеленом фоне.
Чтобы вы окончательно не заскучали от всех этих деталей, ниже картинки всего пайплайна на примере разных юнитов:
В оригинале на сайте можно выбрать и покрутить юнита, переключать разные позы и смотреть, что с ним происходило на каждом этапе обработки. Рекомендую смотреть вживую на всех юнитах.
Дальше будет свет
Пока что я только в общих чертах описал пайплайн для юнитов. Но проблема в том, что даже идеально восстановленные спрайты все еще будут выглядеть плоско, если они никак не реагируют на освещение.
В примерах выше можно подсмотреть, как все работает для освещения: для каждого кадра восстанавливается карта нормалей. Поскольку во все юниты запекли освещение от одинаково расположенного источника света, то с помощью пары хитростей можно его убрать даже без правильных карт глубины (нужно убирать или приглушать оригинальное освещение, потому что иначе затенение будет по сути применяться два раза). Расскажу об этом как-нибудь потом.
В следующих постах я также хотел бы рассказать о том, как сделан ландшафт и разные погодные эффекты, как освещаются юниты и о том, почему Дрега Зул постоянно попадает в пасть к кракену.
Спасибо, что дочитали до конца!
¹ Обещаю не ломать сейвы без необходимости. Только если прямо совсем припрет.
² Надо бы записать эту идею.
³ В итоге он требует минимум 12 ГБ видеопамяти и 24 ГБ оперативки. Сойдет за "обычное железо"?
⁴ Это популярная модель для увеличения отдельных картинок. На ее основе есть много разных модификаций, в том числе для игровых артов и аниме.