Хабрахабр

Встраиваемые языки: почему Lua?

Этот материал продолжает серию публикаций, основанных на докладах, которые мы сделали на конференции Games Gathering 2017 в декабре прошлого года. В одном из докладов была затронута тема выбора встраиваемого скриптового языка.

Что такое и зачем нужны скриптовые языки

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

Как говорится, «в игровой индустрии нет сложных проблем, незачем требовать сложных решений» . Почему вообще возникает потребность в скриптовых языках? И, если честно, нам бы хотелось, чтоб эти программисты были не такие дорогие, а в идеале и вовсе не программисты, а непосредственно геймдизайнеры и сценаристы. Помимо опытных (и дорогих!) программистов, решающих сложные задачи, нам нужны люди (много людей!), которые будут заниматься, скажем, квестами.

Сделаем вывод — что такое для нас скриптовый язык? Таким образом, есть потребность в средстве для описания несложной, но всё-таки логики, без привлечения тяжёлой артиллерии программистов. Дело в том, что для наших целей нам часто нужны управляющие конструкции — ветвление и циклы, в то время как XML это декларативное описание. Это средство, которое позволит сделать разработку игр быстрее и дешевле.
Сразу возникает вопрос, а почему бы нам просто не использовать что-то вроде XML?

И, соответственно, обновлять скриптовую часть игры можно не только вместе с кодом, то есть в ходе обычных обновлений через механизмы магазинов приложений. Ещё одно преимущество скриптовых языков в том, что скрипты в проекте могут быть как кодом, так и ресурсом. Но и вместе с ресурсами — то есть вместе с графическими и прочими материалами, с использованием CDN.

Требования к идеальному скриптовому языку

Сформулируем требования к идеальному скриптовому языку.

  • Динамический. В нашем понимании идеальный скриптовый язык должен быть динамическим.
  • Популярность. Под популярностью языка мы понимаем наличие у него достаточно большого сообщества, готового отвечать на вопросы на специализированных ресурсах наподобие StackOverflow.
  • Пологая кривая обучения. Мы хотим взять, условно говоря, практически любого человека, и быстро обучить его до уровня, который позволит этому человеку продуктивно работать над нашими задачами.
  • Широкие возможности. Язык должен быть мощным и обладать достаточно широкими возможностями, должен поддерживать разные парадигмы программирования. Профессиональный программист, которому предложат писать на таком языке, сможет делать это с комфортом и с удовольствием.
  • Высокая производительность. Производительность — это один из краеугольных камней игровой индустрии.
  • Большое количество библиотек. Очень часто мы, в ходе решения встающих перед нами задач, не создаём принципиально новый код, а пользуемся тем, что уже кто-то написал. Чем больше стабильных, хорошо поддерживаемых библиотек мы можем задействовать, применяя некий язык — тем лучше.
  • Лёгкость встраивания. Речь идёт о встраиваемых языках, поэтому при выборе скриптового языка возможность его встраивания играет важную роль.

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

Python

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

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

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

Теперь рассмотрим JavaScript. В итоге можно сказать, что Python, при всех его сильных сторонах, нам не подходит.

JavaScript

JavaScript — это, без преувеличений, великий язык, который буквально захватил мир.

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

В реальности же придётся выбирать из двух подобных проектов — V8 и WebKit. Если нам, для построения игрового движка, нужен какой-нибудь интерпретатор языка — мы можем найти множество таких интерпретаторов. Как результат, если речь идёт о настольных играх, можно было бы рискнуть и включить в игру весь интерпретатор, но в случае мобильных игр нас такой вариант не устраивает. И тот и другой имеют достаточно большие размеры.

В компании SocialQuantum есть собственный интерпретатор JavaScript, который проходит 98% тестов, мы планируем перевести этот проект в разряд опенсорсных.

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

Haxe

Тут надо отметить, что когда заходит разговор о JavaScript, следующим обычно вспоминают Haxe. Но, на самом деле, о возможности использования этого языка в качестве встраиваемого говорить нет смысла, так как Haxe, по сути, является не столько языком, сколько транс-компилятором в другие языки. А это не то, что нам нужно.

Может быть, нас устроит ActionScript или какой-нибудь другой скриптовый язык?

ActionScript

Если формально проанализировать ActionScript на соответствие вышеозначенным требованиям, то может показаться, что идеальный скриптовый язык найден. На его стороне динамическая природа, популярность, лёгкость изучения, хорошие возможности, производительность, наличие библиотек, лёгкость встраивания. Этот язык любят и помнят в игровой индустрии, на нём написано огромное количество замечательных Flash-игр. Главная проблема ActionScript заключается в том, что язык этот почти мёртв. Поэтому нас он тоже не устраивает.

AngelScript, Squirrel и другие

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

Что если создать собственный язык? Как видно, идеального встраиваемого языка мы пока не нашли.

Создание собственного языка

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

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

Рассмотрев существующие языки программирования, претендующие на роль встраиваемых и обсудив идею разработки собственного языка, перейдём к Lua.

Lua — встраиваемый язык, который выбрали мы

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

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

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

Рабочая среда

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

Возражения по поводу использования Lua

Lua предназначен для C а не для С++

Никто не спорит с тем, что Lua — отличный встраиваемый язык. Главное, что считают его минусом, заключается в том, что он создан для использования с языком C, а не C++. Из-за этого, пытаясь применить в Lua что-то такое, что есть в C++ и нет в C, мы сталкиваемся с проблемами. Однако тут надо понимать, что проблемы эти решало множество довольно умных людей. Среди средств, решающих проблемы встраивания Lua в C++-проекты, можно отметить такие, как Luabind, Luabridge, toLua++, SQLuaHost. Это — далеко не полный список. Они обладают разными достоинствами и недостатками, но, тем не менее, скорее всего, всё, что вам может потребоваться, уже реализовано в одном из этих решений.

Это — биндинг, который сделан внутри компании SocialQuantum, и который планируется сделать опенсорсным. Рассмотрим, например SQLuaHost. Поэтому, вполне возможно, что если вы не нашли то, что вам нужно в существующих биндерах, вы найдёте это в SQLuaHost. Это решение представляет наше видение того, как должен биндиться Lua.

Lua — это медленно

Нам часто приходится сталкиваться с мнением, в соответствии с которым Lua — это очень медленный язык. Во-первых — это не так. Lua — это стековая машина, и там, на самом деле, просто нечему тормозить. К тому же надо понимать, что в скриптовый язык мы обычно отдаём игровую логику, бизнес-логику, а не какие-то действительно тяжёлые вещи. В результате, если Lua-скрипты заставляют игру тормозить, то проблема, возможно, кроется в неоптимальном биндинге или в нерациональном использовании каких-то функция языка. Мы, например, проводили синтетические тесты, на которых LuaJIT работает быстрее, чем Mono. При этом никто не мешает писать примерно такой вот неоптимальный код:

function myGame:onUpdate() local tex = Texture::new(name) self.background:setTexture(tex)
end

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

Lua подходит только для маленьких проектов

Следующее возражение заключается в том, что Lua сделан для того, чтобы писать какие-то маленькие вещи и что-то большое на этом языке написать невозможно. С одной стороны это правда. Но у этого языка высокая модульность. И из множества маленьких блоков можно делать достаточно большие и сложные системы. А если вспомнить то, что было уже сказано о мультипарадигменности и об ООП, то окажется, что ООП подталкивает разработчика к тому, чтобы создавать маленькие модули, которые можно использовать при создании больших и сложных конструкций.

При этом зачастую на Lua какие-то маленькие модули пишутся быстро, а в игровой индустрии «быстрее» — значит «дешевле».

Другие аргументы против Lua

Критикуя Lua, говорят о том, что язык это древний, что он, что называется, «из коробки», не поддерживает ООП, что нумерация элементов в его таблицах начинается не с 0, как можно было бы ожидать от любого приличного языка, а с 1.

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

Итоги

Подведём итоги. Если ваша задача — с минимальными усилиями обзавестись встраиваемым языком — возьмите Lua. В то же время, если у вас есть время и ресурсы на разработку собственного языка или собственных биндингов — опять же — обратите внимание на Lua. Почему и в первом и во втором случаях мы рекомендуем Lua?

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

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

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»