Как стать автором
Обновить

Миграция моей игры с Rust

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров10K
Автор оригинала: Brandon Reinhart
Banner

Когда я начал работу над Architect of Ruin в декабре 2023 года, то решил делать её в игровом движке Bevy. Мой выбор был мотивирован собственным интересом к Rust — языку, от работы с которым я получаю много удовольствия. Ещё больше меня привлекла в этом движке модель ECS, с которой мне тоже нравится работать, а также сообщество Bevy, которое мне очень симпатично.

Поэтому стало неожиданностью то, что в январе 2025 года мы выполнили миграцию нашей игры с Rust и Bevy. Я потратил примерно шесть недель, чтобы полностью переписать игру на C#, и последние три месяца мы работаем в Unity.

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

Но мы решились на это, и в посте я объясню, почему.

Наш путь с Bevy

Мы проделали в Bevy много хорошей работы. В нём были реализованы тайловая карта, основная доля обработки сцены и большая часть логики персонажей и геймплея. Я многое узнал о внутреннем устройстве Spine и скелетной анимации, разобрав транспиляции Rust среды выполнения spine. Мне удалось многому научиться в области конвейеров рендеринга благодаря реализации собственных фич рендеринга в render world Bevy. С чистым ECS этого движка было приятно работать, а с помощью проверок Rust на этапе компиляции я мог быстро и уверенно рефакторить большие блоки кода.

Кроме того, активным источником вдохновения для меня стало сообщество Bevy — мне не только помогали с идеями об использовании движка, но и шло на пользу само позитивное сообщество творцов и контрибьюторов. Это сообщество сильно поддерживает увлечённость разработкой игр и стимулирует к обсуждениям.

Мне представилась возможность контрибьютить фичи и исправления во множество крейтов сообщества; впрочем, основная часть этих изменений была небольшой и связанной с моим личным проектом.

Однако несмотря на положительный опыт и прогресс, у меня возникли практические трудности.

Всплывающие проблемы

Хочу начать с заявления о том, что я предвидел многие эти сложности ещё до их возникновения. Я знал, что использование игрового движка, находящегося на столь раннем этапе разработки, представляет уникальные риски и накладывает дополнительные затраты. Я посчитал эти затраты стоящими риска и вполне приемлемыми. Из-за своей любви к Rust и Bevy я был готов к неудобствам, которых стремятся избежать другие разработчики игр. Я не шёл к этим проблемам вслепую, но они оказались чуть сложнее, чем я ожидал.

Совместная работа — я начал этот проект со своим братом. Он умный и целеустремлённый, но новичок в кодинге. Знакомство его с разработкой игр и параллельное объяснение уникальных аспектов Rust оказалось достаточно сложной задачей. Кривая обучения оказалась довольно крутой, из-за чего он меньше мог вносить свой вклад в геймплейную логику.

Абстракция — хоть изначально я был мотивирован радостью работы с Rust, всё более узким местом проекта становились быстрые итерации высокоуровневых геймплейных механик. С ростом объёмов кодовой базы выяснилось, что преобразование геймплейных идей в код оказалось менее прямолинейным, чем мы надеялись. Мощные низкоуровневые возможности Rust не всегда подходят для гибкого высокоуровневого «скриптового» стиля написания кода, необходимого для быстрого прототипирования конкретно в нашей архитектуре геймплея. Я понял, что моя мотивация к созданию увлекательного геймплея была сильнее, чем желание писать на Rust.

Я предвидел, что так произойдёт, но оказался не готов к тому, насколько это начнёт раздражать меня и замедлять работу над проектом.

Rust can be verbose.
Сигнатура относительно простой геймплейной функции

Миграция — Bevy молод и быстро меняется. Каждое обновление приносит потрясающие фичи, но в то же время и существенные изменения в API. С ростом размеров проекта тяжелее становилось и бремя миграций между обновлениями. В базовых системах Bevy (например, в рендеринге спрайтов) часто возникали небольшие регрессии, что приводило к сильным затруднениям и неожиданно долгой отладке.

Однажды мне пришло в голову, что меня бесит проблема рендеринга спрайтов, возникшая в новом релизе. Мой брат Блейк в то же самое время столкнулся с той же проблемой, и наше общее раздражение привело к серьёзным переменам. Мы поговорили и пришли к переоценке своих взглядов.

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

Обучение — за прошлый код мой процесс работы существенно поменялся; я активно использую ИИ для изучения новых технологий, обсуждения способов и методик, код-ревью и так далее. Благодаря зрелости и большому объёму стабильных исторических данных о C# и Unity API инструменты наподобие Gemini дают хорошие советы и инструкции. Bevy и Rust стремительно развиваются, что восхищает и мотивирует, однако из-за этой скорости знания ИИ отстают, что снижает эффективность разработки с его помощью. Ситуация может поменяться с появлением более современных моделей, но я посчитал это помехой и неожиданными дополнительными затратами.

Моддинг — моддинг для меня очень важен. Я начал свой путь в этой отрасли с моддинга, поэтому хочу, чтобы мою игру можно было очень сильно модифицировать. Со временем я больше узнал о том, как реализовать эту цель, и понял, что многие неотъемлемые ограничения Rust и Bevy усложнят эту задачу. У меня вызывали опасения отсутствие надёжного решения для реализации скриптинга и нестабильный ABI (application binary interface). Я не специалист в этой сфере, так что, возможно, все эти проблемы легко решаемы. Могу только сказать, что после долгих поисков я не нашёл нужного пути, которому бы мог доверять.

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

Переход

Честно говоря, когда я приступал к проекту, то полностью проигнорировал Unity.

Частично это было вызвано ошибками со стороны Unity. Движок недавно прошёл через кризис ценообразования, кульминацией которого стало увольнение CEO; к тому же, компания как будто потеряла общий язык с инди-разработчиками. Кроме того, у меня было множество необоснованных допущений. Я испытывал отвращение к написанию кода на устаревших версиях C++ в древних игровых движках, и мне казалось, что к C# у меня будут схожие чувства. Я предполагал, что поскольку в Unreal практически нет конвейеров 2D-рендеринга, то их нет и в Unity. Из-за всего этого в 2023 году я и не задумывался серьёзно об использовании Unity.

В первую неделю января 2025 года мы с Блейком решили провести анализ, составив список вариантов: Unreal, Unity, Godot, продолжать работу в Bevy или выкатить собственный движок. Мы подробно описали все плюсы и минусы, сделав упор на том, как каждый из этих вариантов повлияет на перечисленные выше критерии: удобство совместной работы, абстракции, миграцию, обучение и моддинг.

У меня был некоторый опыт с другими вариантами, поэтому я решил, что мне нужно лучше разобраться в Unity. Я исследовал вопрос и пришёл к выводу, что у этого движка гораздо больше плюсов, чем минусов.

Мы провели совещание, на котором я изложил список компромиссов. Ульрик считал, что множество неизвестных факторов, например, проблему частиц, можно решить при помощи движка, в котором они есть. Блейк сказал, что если всё пойдёт хорошо, новый движок ускорит разработку геймплея, и мы можем опередить наш график.

10% за 90%

Наша команда решила вложиться в эксперимент. Я выбрал три базовых фичи и проверил, насколько сложно их будет реализовать в Unity. На эту задачу нам предстояло потратить не больше трёх недель. Мы были готовы вложить 10% усилий, чтобы понять, нужно ли вкладывать оставшиеся 90% в портирование игры целиком.

Тайловая карта — я решил, что эта фича будет несложной, потому что проста базовая логика. Потребуется реализовать собственные шейдеры. Мы не будем использовать встроенную Tilemap Unity, потому что наши потребности специфичны и хорошо нам известны.

Персонажи — мы создавали персонажей в Spine, они обладают уникальными требованиями к кастомизации и фичам. Это вызвало у меня множество проблем на Rust, поэтому я решил, что будет подходящий аспект для сравнения с C#.

UI — мне хотелось, чтобы UI был прост в создании, с ним можно было быстро проводить итерации и его можно было менять моддингом. Эту область мы хорошо изучили на Rust, поэтому у нас имелась качественная ментальная модель для сравнения. Мои исследования привели к выводу, что хорошо подойдёт Noesis, потому что он делает упор на управляемый данными XAML, а также из-за того, что модель WPF очень хорошо задокументирована. Хоть я и не знал WPF, но понимал, что смогу быстро выучить его с помощью ИИ.

Первые две задачи, тайловые карты и персонажи, были выбраны потому, что они фундаментальны для игры, а ещё потому, что они позволят мне проверить мои ожидания в плане сроков выполнения простой и сложной задачи. Благодаря этому я смогу прогнозировать рабочую нагрузку будущих задач и портирования в целом. Задача UI была выбрана, потому что в нашей игре активно используется UI и любое ускорение итераций разработки UI даст большую выгоду при разработке в будущем.

Мы справились со всеми тремя задачами всего за три дня!

Коммит 1 был создан 8 января, и в тот же день мы закончили тайловую карту. 

Пока я реализовывал тайловую карту, Блейк трудился над системой камеры. Это существенно повысило полезность его вклада в условиях более понятного технического фреймворка. К тому же это сильно поспособствовало его уверенности и придало ему скорости. Стоит отметить, что Блейк раньше ни разу не писал на C#.

Я реализовал шейдер тайловой карты на Unity Shader Graph, подумав, что Ульрику будет проще с ним работать. Это был не последний написанный мной граф шейдеров, но в конечном итоге я решил, что визуальное создание шейдеров и итерации с ними — это слишком медленно, а рефакторинг происходит ещё медленнее. Теперь я пишу все шейдеры на HLSL.

10 января мы разобрались с основами создания UI в Noesis. Блейк написал несколько простых виджетов UI, а затем создал главное меню, а я собрал первую часть HUD игры — панель инструментов.

Работа шла гораздо проще, чем я думал, и нам не пришлось ничем жертвовать. На тайловую карту ушёл день, простую панель в Noesis мы сделали за полдня, в том числе и подключили плагин. Остальную часть времени я подключал персонажей. Я не говорю, что портировал весь UI за полдня или реализовал всю систему экипировки. Мы создали настраиваемые тела персонажей, полностью портированную тайловую карту и несколько простых меню. Плюс набрались достаточно знаний, чтобы прогнозировать, сколько времени понадобится на остальную часть порта.

Very representative of the end of that first week.
Это изображение даёт представление о том, чего мы добились к концу первой недели портирования, хотя скриншот сделан спустя несколько дней, когда мы добавили в игру предметы

К концу недели мы собрались, чтобы обсудить то, чему научились, и приняли решение заняться полным портированием.

Следующие шесть недель были посвящены переписыванию оставшихся систем и контента с Bevy на Unity/C#. В целом этот процесс подтвердил выводы, сделанные после трёхдневного теста. Геймплейные системы с тем же количеством фич можно реализовать меньшим количеством кода. 

Беседа 4 марта 2025 года
Беседа 4 марта 2025 года

Объём кода существенно снизился, что сильно повысило удобство поддержки. Насколько я могу судить, основная часть этой экономии вызвана просто устранением бойлерплейта ECS.

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

Жизнь после перехода

Мы разрабатываем Architect of Ruin на Unity уже три месяца. Смена движка существенно повысила удобство повседневной разработки. Итерации происходят быстрее, что позволяет проще добавлять в игру новые идеи. Кроме того, мы смогли использовать инструменты экосистемы наподобие AStar Pathfinding Project.

Область, в которой проблемы не были решены и которая, скорее всего, будет стоить нам некоторых неудобств — это локализация. В Rust есть проект Fluent, делающий ровно то, что нам нужно. Пока мы не нашли сравнимого решения для Unity.

В будущих постах я планирую рассказать о конкретных элементах реализации игры на Unity и процесса портирования. В этом посте я просто хотел объяснить причины, которые привели к нынешнему состоянию дел.

Можно сделать несколько важных выводов:

Я не оценил варианты выбора в начале проекта объективно. Rust прекрасен и я люблю его, но я не дал альтернативам справедливого шанса. В частности, я не уделил достаточно времени изучению различий между Unreal и Unity.

Иногда нужно потратить время, чтобы сэкономить время. Я думаю, что мы ушли гораздо дальше, чем в случае, если мы остались на Bevy. Кроме того, сильно повысило качество геймплея возможность реализации фич рендеринга.

Rust остаётся языком, от которого я получаю огромное удовольствие, а Bevy — это потрясающий движок с замечательным сообществом. Я глубоко уважаю и тот, и другой и вполне могу использовать их снова для других проектов. Однако в случае Architect of Ruin нам требовалось удобство совместной работы, быстрые итерации разработки геймплея и использование стабильной экосистемы.

Это решение было трудным, оно противоречило моим инстинктам, но в конечном итоге обеспечило нам гораздо более сильную позицию в реализации нашего видения игры.

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+35
Комментарии15

Публикации

Работа

Ближайшие события