7 октября 2022

Добавляем Lua в Unity. Стоит ли?

В моём Telegram-канале был пост про интеграцию Lua в Unity в качестве языка для скриптинга. Спустя время хотелось бы рассказать об этом более подробно.

Использовать особенности скриптового языка можно по-разному - сделать поддержку модов, ускорить итерации разработки, дать геймдизайнерам возможность писать простую игровую логику самостоятельно, сценарии миссий, редактор диалогов и т. д.

Плюсы

Скорость итераций

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

Разделение ответственности

Основа игры всё ещё написана на языке движка - в случае с Unity это C#. И только сценарии игрового процесса используют скриптовый язык. Это может быть поведение врагов, диалоги, кат-сцены и прочие штуки. При таком подходе, отдать реализацию этих вещей можно менее квалифицированному специалисту: во-первых, Lua проще. Во-вторых, доступ у него будет только к необходимому API, который проще и быстрее выучить. А так же, за счёт этого, вероятность что-то сломать в игре гораздо ниже. В целом, такая изоляция основной кодобазы позволяет хорошо разделить фичи на ключевые и на те, которые относятся конкретно к игровому процессу (сценарии, диалоги и т. д.).

Легче добавить моддинг

Подключение Lua позволяет минимальными затратами дать игрокам доступ к созданию модов, влияющих на геймплей. Так же важно, что мододелы хорошо знакомы с Lua по другим играм, в которых он используется для моддинга.

Легко интегрировать за счёт готовых библиотек

Используя, например, NLua, мы получаем полную интеграцию всех основных фич C#. Это значит, что классы и методы из C# будут доступны и в Lua почти без трудозатрат, а это открывает широкие возможности для работы в Lua c API игрового движка (при необходимости).

Готовые IDE и редакторы кода

Не нужно тратить время на разработку такой оболочки, как пришлось бы в случае с визуальным редактором, например. Пользователь или мододел сам выберет, в чём он будет писать код. Так же луа хорош тем, что можно писать код даже в блокноте.

Минусы

Поддержка платформ

Хотя Lua и поддерживается почти на всех платформах, именно связка Unity + Lua может стать проблемой. Поэтому хорошей идеей использовать Lua будет только если ориентируетесь на минимальный набор разных платформ. Идеальный вариант - Windows. Хотя можно посмотреть такое решение как UniLua, хотя лично мне оно не понравилось, показалось перегруженным ненужными мне функциями.

Зависимость от чужих библиотек

Для интеграции Lua в C# проще всего использовать чужие библиотеки, например - KeraLua или NLua. Но это добавляет зависимость от библиотеки - например, может обнаружиться отсутствие жизненно необходимого функционала, баги или прочие непредсказуемые проблемы. Написание собственной связки C# + Lua вряд ли оправдает трудозатраты.

Поддержка связи между C# и Lua

Игровой API, который будет доступен в Lua, необходимо описывать в C#. Чем больше API будет сделано доступным в Lua, тем труднее будет его поддерживать со временем.

Безопастность

Lua весьма мощный язык, и злоумышленники вполне могут использовать его в своих целях. Например, в игре Garry's mod через уязвимости реализации Lua можно получить доступ к ПК игрока. В Dota 2 используя возможности Lua были созданы весьма мощные читы.

Усложняется восприятие проекта

Разделение на два языка может усложнить понимание проекта для новых людей в команде, и в целом всю кодобазу будет труднее держать в голове. Это общепринятое мнение, которое с моим не совпадает. Я считаю, что например блюпринты никак не мешают движку Unreal, а наоборот, сильно упрощают и ускоряют работу с ним для многих людей, в том числе не-программистов. А ведь блюпринты это почти тоже самое, что и скриптовый язык.

Скорость работы

Выполнение Lua-скриптов вероятно будет менее оптимизированным, чем то же самое на C# или ещё более быстром языке. Если целевая платформа ПК, не считаю минус критичным.

Альтернативы

У связки Lua + Unity всё-таки достаточно минусов, чтобы отказаться от решения интегрировать Lua в движок. Но можно подумать об альтернативах.

Не использовать скриптовый язык вообще

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

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

А чё Lua? Может, Python?

Неплохая, хотя и очень поверхностная статья на эту тему - "Встраиваемые языки: почему Lua?".

Если коротко, Lua показывает себя весьма неплохо, и является частым выбором других разработчиков (WoW, Garrys mod, Dota 2 и ещё сотни других игр). Вероятно, не просто так.

Свой итерируемый скриптовый язык

Считаю, что в реализации это сложнее, чем интегрировать Lua. Но это избавляет от проблем зависимости и поддержки на разных платформах (при правильной реализации). Скорее всего, такой язык будет гораздо более ограничен в плане доступа к API C# и самой игры, но это может стать плюсом, поскольку упростит понимание языка до максимума, а так же полностью изолирует все опасные возможности по работе с API, которые есть в Lua.

Вариант для хардкорщиков, не считаю его достаточно эффективным. Ну типа нафига, если уже есть куча готовых вариантов?

Визуальный редактор

Визуальный редактор, он же нодовый редактор - позволяет вместо написания кода соединять "ноды", то есть блоки функций. Это вполне себе альтернатива скриптовому языку.

В Unity до сих пор нет нормального визуального редактора кода. Конечно, есть Bolt, но он довольно слаб по сравнению с Blueprints из Unreal. К тому же, оба эти решения имеют несколько другую задачу, они по сути дают полный доступ к API движка, что почти не упрощает жизнь и не решает никакой конкретной задачи кроме плацебо-эффекта для людей, которые боятся писать код (в случае с Blueprints так же решается проблема ускорения итераций и реально снижается сложность, т. к. C++ это вам не шуточки).

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

Для реализации есть два пути.

Editor-time редактор

Данный вариант реализуется через скрипты расширения редактора Unity и позволяет добавить своё окошко с нодовым редактором в движок. Это самый удобный вариант для разработки, учитывая, что в Unity есть API для реализации нодовых редакторов - Node Graph Processor.

Это супер-решение для вашей команды и одна из лучших альтернатив Lua. Но это решение отсекает возможность поддержки моддинга через этот инструмент (если не открыть исходный код игры) и, вероятно, возможность запуска своих скриптов прямо во время игры. Короче, подойдёт не для всех задач.

Runtime-редаткор

Ещё интересный вариант редактора, который будет использовать игровой UI и выполняться прямо во время игры (допустим, разместить это на специальной сцене с редактированием игровой логики).

Основной плюс - тут мы избавляемся от недостатков Editor-time редактор, появляется возможность отдать инструмент моддерам и легко запускать скрипты во время игры. Но главным минусом для меня тут выглядит отсутствие готового решения для нодового редактора, придётся реализовывать его самостоятельно и это вряд ли стоит усилий. Готовые либы есть, например UnityRuntimeNodeEditor, но этого может не хватить.

Выводы по визуальным редакторам

Общий недостаток - многие фичи придётся реализовывать самостоятельно (например циклы, ветвления), в то время, как в Lua всё это есть из коробки. И в зависимости от необходимого функционала, эффективность этой альтернативы может сильно отличаться.

Несмотря на минусы, я бы рассматривал это как главную альтернативу скриптовому языку.

Запустить C# в Runtime!

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

Итог

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

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