Отчет: Перенос увесистого инди Lua-проекта на Unity малой кровью
Внимание! Этот документ ещё не опубликован.
Автор: kkolyan
Что-то непогодится в моих краях. Да и год прошел - можно подводить итоги.
Наверняка все видели амбициозный проект "Crossroad In The Void: Олдскульная партийная рпг", ведущийся эксцентричным, но пытливым разработчиком уже чуть более двух лет.
Раньше выглядел он так:

Предисловие
Что-то нужно менять
А какой есть выбор?
JMonkeyEngine
Urho3D
UE
Stride
Unity
Развитие игры на новой платформе
Тулсет и свобода
Актуальный технологический стек
Послесловие
Предисловие
Чуть забегая вперед, раскрою прежний технологический стек проекта:
1. LÖVE (aka love2d). Это добротно сделанный фреймворк, написанный на C++ для написания 2D игр на Lua.
2. 3DreamEngine. Это расширение для LÖVE, целиком написанное сторонним разработчиком на Lua, превращающее LÖVE в простенький 3D-движок. т.е. рэйкастинг, загрузка моделей (OBJ), а также рендеринг, основанный на способности LÖVE выводить на экран многоугольники.
3. сторонняя библиотека для GUI написанная на Lua как расширение для LÖVE.
4. Ядро - много... нет, МНОГО луа скриптов, написанных автором. игромеханически игра находится в состоянии полнофункциональной альфы - по большому счету, фич можно уже не добавлять и только фиксить баги. Хотя что-то еще добавляется.
5. Визуальный контент. OBJ + текстуры. Любые эффекты - через код.
6. Общий контент. Карта статических объектов составляется в Tiled Map Editor, а вся динамика (положения и свойства NPC, ловушек, телепортов, сундуков, квесты, локализация) прописана в специальных декларативных Lua-файлах (похоже на JSON).
можете осуждать его за странный выбор технологий, но пока мы рендерили чайники, человек делал игру мечты на тех инструментах, которыми владеет. и по местным меркам огого как продвинулся, да и все еще не слился!
Как известно, Lua - язык довольно медленный. И даже LuaJIT (который как раз и входит в состав LÖVE) не способен спасти ситуацию, когда на луа начинается не расчет кол-ва урона, а рэйкастинг и рендеринг моделей, где буфер вершин представлен в памяти структурой вроде "shared_ptr<unordered_map<int, shared_ptr<unordered_map<string, float>>>>". Игра тормозила в самых неожиданных местах, при этом выглядя, мягко говоря, очень просто.
Но производительность - это только часть проблемы. Несмотря на нишевый жанр и очевидный долгострой, проект привлекает художников, а иногда и очень хороших. А художники любят делать картинку которая им нравится, пусть даже речи о High-end графике не идет. Описанный техстек во-первых накладывает дичайшие ограничения на сложность сцены, а во-вторых предлагает очень скудные возможности работы со светом, анимациями и эффектами. фактически художник может только предоставлять модели (без анимаций) и текстуры, а эффекты и свет просить кодить программиста, а то и самому в скрипте редактировать. В общем, художники страдают от таких тесных рамок выразительных средств.
Что-то нужно менять
Будучи старым поклонником трилогии рпг-шек Might & Magic VI-VIII (в свое время на пару с The Elder Scrolls раздвинувшими стандарты размеров игровых вселенных), с чешущимися руками раз за разом проходил мимо треда CITV. И раз за разом в личке общался с автором по техническим около игровым вопросам - как вообще живется и какие взгляды у автора на то "что хочется" и "что можется". И все больше понимал, что технология, применяемая автором сильно ограничивает рост проекта и лучше что-то поменять. Автор в свою очередь резонно предложения о смене техстека отметал - ведь он делает игру мечты, а не экспериментирует со стеком, тем более игра изначально типа-олдскул, а значит графические улучшения не в приоритете.
Прошлым летом я психанул и понял что на это смотреть больше нельзя. Проект нужно технически апгрейдить чтобы удержать/привлечь художников и дать на время забыть о тормозах. Но речи о переписывании игры на U* быть не может, т.к. никто этим заниматься не будет. С таким же успехом можно бы и просто свою РПГшку запилить с нуля, оставив автора этого проекта в покое.
Но есть решения и более ковровые. По сути, большая часть проблем в API 3DreamEngine, а его API довольно небольшой (используемая в проекте часть). LÖVE побольше, но тоже не такой уж большой(та часть, которая используется в Ядре ). надо просто заставить Ядро запускаться в ином окружении с минимальными изменениями скриптов. В окружении с более матерым движком вместо 3DreamEngine (но мимикрируя под его API).
В общем, попросив исходники игры у автора, но ничего не обещая, стал экспериментировать.
А какой есть выбор?
JMonkeyEngine
Поясню, я не настоящий программист и моя вотчина - java (бэкенд для энтерпрайза). Так что всерьез рассматриваю и такие варианты (к тому же есть опыт в близком жанре, показывающий что ява в какой-то степени применима). Возможно многие удивятся, но при определенной сноровке java разгоняется до половины скорости крестов. И реализации интерпретатора Lua на java весьма быстрые по сравнению с C# аналогами (но луаджиту тоже сильно сливают). Тем не менее, JMonkey предлагает довольно скудный тулсет, и кроме возможного (но непростого в достижении) буста в производительности здесь ловить практически нечего.
Urho3D
Я не мог не рассмотреть какой-нибудь крестовый двиг, т.к. было интересно пощупать кресты обстоятельнее, а к тому же есть потенциальная опция срезать углы - оставить LÖVE для 2D и рендерить 3D другим движком, шаря окно/холст/хз_что. Отличная была идея, и суппортеры урхи (вернее RBFX) очень отзывчивые ребята, но через пару недель ковыряния в исходниках LÖVE, доках/туториалах по OpenGL и попытках найти/воспроизвести желаемый шаринг, у меня отклеился ус и началась аритмия. В другой раз...
UE
Ну тут понятно - high-end ассеты - это не олдскульно ни разу, кресты вызывают аритмию, а блупринты даже для java-программиста - как-то уж слишком.
Stride
Шарп, но не нашел причин выбрать его, а не Unity. Может быть когда-нибудь потом, когда Unity изменит лицензию или совсем скатится.
Unity
Старался максимально убедиться, что вариантов больше нет, прежде чем начать работать в эту сторону, т.к. в успехе этого варианта я был практически уверен. C# для java-программиста как родной. Да и ну вы сами понимаете, художники в юньке могут сами эффекты делать и свет настроить.
Окей, выбрал Unity. MoonSharp (Lua интерпретатор на C#) очень тормознутый. Причем IL2CPP не помогает ничем - скорость такая же в рамках погрешности что и у Mono (это для тех, кто считает, что IL2CPP для скорости). NLua/KeraLua (готовые биндинги к нативному интерпретатору) несовместимы с LuaJIT т.е. тоже медленно, к тому же предоставляют слишком high-level обертки, что тоже обязательно сворует скорость и затруднит работу с документацией Lua API. Но очень быстро получилось заэмбеддить LuaJIT с помощью P/Invoke без единой строчки на C++. Вынес простые низкоуровневые биндинги в отдельную либу, кому интересно (поддерживает удобный просмотр скриптовых объектов при отладке C#) и поддерживаю по мере возникновения проблем.





