![image](https://habrastorage.org/webt/xx/5l/zz/xx5lzzb-posrr_pnbvchdja6nbq.png)
Это небольшой разбор игрового движка Defold на практическом примере(под Andoid), а также немного субъективной критики и похвалы, ну вообщем обзор. Кому интересно узнать об этом двигателе больше, есть одно интервью, почитайте. Информации о Defold в сети мало, так что это будет вклад в очень небольшую копилку.
Немного прелюдии
Итак, Defold — это сравнительно молодой(с 2011 года), кросс-платформенный двигатель с визуальным редактором, похожим на Gobot, Construct или Cocos Creater. В основном он предназначен для 2D (для работы с 3D все еще не хватает инструментов), язык программирования — Lua. От аналогичных двигателей отличается стабильной производительностью на мобильных(ровные 60 fps без лагов со звуком), небольшим размером билда(2-4 мб в зависимости от платформы), удобной системой gui и go, крутые настройки анимации(кривые ключей), а также полностью подчиняемым редактором(ниже расскажу, интрига). На сайте, под учетной записью, есть возможность хранить файлы проекта. Вносить изменения в проект может любой разрешенный член команды, что в общем-то удобно, хотя я сам не всегда мог этим пользоваться, т.к. наш рабочий прокси не дает редактору доступ. У Defold'а есть особенности в написании кода, хотя их я бы отвел к минусам(подробности ниже). И да, чуть не забыл, команда Defold очень отзывчивая, заслуживает уважения. Несколько раз обращался за помощью на форум — тут же, в течении 10 минут отвечали, помогали. Один раз была проблема при запуске редактора на Ubuntu, ребята не могли сначала помочь, но, через пару часов со мной связался их лидер и разрешил ситуацию. Оказалось, на linux есть баг со стандартными дровами на видео, так что надо установливать родные. Также на сайте есть ассеты, куда же без них. Ну, закончим прелюдию, поехали!
Стартуем!
Регистрируемся и скачиваем на сайте редактор Defold версии 2 (1 уже не поддерживается), там же создаем свой проект и скачиваем. Проект затем храним на сайте, либо локально (если боимся, что хитрый Дефолд будет подглядывать). Далее открываем через редактор свой проект(на картинках будет мой), смотрим:
![](https://habrastorage.org/webt/ui/jh/em/uijhemq6p_7__xe1j0ivkhtdngm.png)
Слева файлы, справа внутренности выбранной ноды, посередине редактор. Не буду увлекать вас разбором интерфейса, его можно изучить самому. Видите папку builtins? Там стандартный набор скриптов, шрифтов и прочего для работы двигателя, можете изучить. Все остальное — ваши собственные файлы. В настройках проекта(на картинке посередине) есть строчка(выделенная) Main collection, там ссылка на основной файл коллекции, с которого начинается ваше приложение, откроем его:
![](https://habrastorage.org/webt/gs/la/xx/gslaxxxplapjspuyfqbudzez0tc.png)
Да, да, именно такой бардак будет у вас в редакторе, ведь нет возможности скрыть ненужное(зачислим в минусы). Давайте сначала разберемся, что такое коллекция. Когда я выше писал про подчиняемый редактор, имелось ввиду, что каждый файл проекта, неважно, какого он расширения, является текстовым, и его можно редактировать самостоятельно. Так вот, коллекция — это тоже текстовый файл с описанием внутренних объектов.
![](https://habrastorage.org/webt/o9/bp/l_/o9bpl_bxsy0dbia1z0d9rufmlne.png)
Коллекция может содержать любые объекты(скрипты, спрайты, звуки и т.д), а также вложенные в нее другие коллекции. Все ее содержимое при выполнении будет создано, скрипты инициализированы. Коллекции можно оборачивать в Proxy. Proxy — отличная штука, в них обычно оборачивают различные уровни вашей игры, например. Их можно отгружать, загружать(при загрузке происходит создание вложенной коллекции), ставить на паузу, замедлять, ускорять процессы.
Также, очень удобная штука — Factory и Collection Factory. Это оболочки для объектов, которые незаменимы, если необходимо создавать и удалять объекты динамически, в процессе исполнения.
![](https://habrastorage.org/webt/oz/rk/p0/ozrkp0lrpmebpqu0tjqahqutfy8.png)
Вот наглядный пример динамической коллекции. Это шип, он подгружается случайным алгоритмом на уровне и удаляется, стоит ему выпасть из обзора камеры, чтобы не тратить ресурсы.
![](https://habrastorage.org/webt/dg/88/cr/dg88crfsnwqfeoo2s1nw9rmp9hy.png)
Это скрипт внутри шипа, в методе update он проверяет, если собственное положение меньше, чем положение персонажа(игрок в центре камеры) плюс небольшой запас, то удаляет сам себя. Кстати, на скрипты можно вешать свойства. Например у меня есть объект factory collection, внутри которого овца npc.
![](https://habrastorage.org/webt/aw/r4/ti/awr4tivsyzwqubh8gb6cl0-fzqe.png)
Вот наш npc, у него на скрипте есть такие свойства, как скорость бега, шага, силы прыжка и выносливости. При создании экземпляра, я рандомным способом прописываю ему эти свойства.
Cоздаем npc:
![](https://habrastorage.org/webt/rn/oi/o1/rnoio1acdrjioup3f0ykqi9ijr8.png)
Внутренний экзмпляр скрипта меняет свои свойства(уникальность через self.):
![](https://habrastorage.org/webt/9w/tc/s6/9wtcs6a554em8e2wuzab6foywi0.png)
И вот наши овечки бегут с разной скоростью:
![](https://habrastorage.org/webt/m2/up/f3/m2upf3hky56ircljuxhoily8a1s.png)
Кстати, о редакторе кода — он примитивный. Методы нельзя сворачивать(как например в Sublime), а когда строчек становится много, приходится долго и мучительно крутить колесом, выискивая нужное. Также нет увеличения масштаба, но это мелочи. В ассетах Defold есть инструкция, как приучить Sublime к заполнению методов из редактора.
Что касается частиц, есть удобство — если в окне редактора выделить эмитер и нажать пробел, то можно сразу увидеть результат.
Ну, а теперь о том, как работает движок
Defold принимает на рендер две отдельные ветки, это GUI и GO.
GUI — как уже понятно из названия, это интерфейс пользователя. Он существует отдельно, настраивается по экрану и всегда рендерится поверх всего остального. GUI имеет свои собственные методы, не имеющие отношения к GO, хотя и сходные с ним. Это как бы тоже коллекция, но с ограничениями. На один GUI принимается один скрипт, с расширением .gui_script. Отличная и удобная штука, хотя есть нюанс — из GUI нельзя напрямую обратиться к каким-либо игровым объектам, только через глобальные переменные, либо через сообщения(дальше расскажу).
GO — это игровой объект (что-то типа ноды трансформации), содержит информацию о перемещении, повороте, масштабе и т.д. Во внутрь принимает все, включая другие GO. Используется для коллекций, это обязательная оболочка для любого количества объектов(как бы группа).
Дальше, начинается самое интересное. На Defold я пересел из дружелюбного Android Studio с Libgdx, и вот, что меня потрясло: к объектам нельзя обратиться напрямую, только через костыльные методы или через сообщения, которые все равно, не дают творческой свободы, есть серьезные ограничения. Если сравнивать с тем же Libgdx, там я спокойно мог даже переписать существующий класс из библиотеки Либа и обратиться уже к нему. В Дефолде, увы пользуешься тем, что есть, и, если чего не хватает, просишь разработчиков добавить необходимую функцию, либо ищешь обход. Например, при работе со звуком, нельзя узнать закончилось ли воспроизведение! Пришлось добавлять костыль, узнающий длительность звука по воспроизводимому файлу. А что касается упрощенного Box2D(внезапно понеслась критика), так оттуда исчезли славные методы по обнаружению и обработки перед, во время и после столкновения! Вместо этого, в скрипт, который находится в одном GO с физическим телом, прилетает сообщение(не всегда каждый кадр, особенно на мобильном) с информацией о столкновении, и все. Также нельзя перемещать тело, если оно физическое, можно только кинематическое, а менять это свойство во время исполнения нельзя. GO нельзя масштабировать вместе с физическим телом, в редакторе оно у вас поменяет масштаб, а в игре нет(вроде обещали исправить). GO нельзя масштабировать в минус(например отзеркалить по X), в редакторе все получится, но в исполнении вернется на исходную. Вообщем, я немного побунтовался на этот счет и (решив эти проблемы через костыли) успокоился, все-таки скорость Дефолда и его редактор перевесили тормозной Libgdx(Да простят читатели мне мою субъективность!) и отсутствие нормального редактора(Помню как-то смастерил под Либ редактор карт в Autodesk Maya, но это уже совсем другая история...).
Метод msg.post() и как к нему относиться
Вообщем, у каждого объекта есть url(например «main:/main_pers#spine_anim»), по которому можно прислать сообщение, и даже вместе с ним прикрепить переменные. Сообщения летят к адресату в напрямик, не ожидая в очереди на обработку. Это и удобно, и также создает путаницу иногда. Сообщения часто нужны для того, чтобы переслать информацию из GO в GUI(например, ваша овечка поймала пузырик с фруктом, обновляем счет очков в GUI).
![](https://habrastorage.org/webt/cl/rm/qp/clrmqpozdciyhz7h-g9ogemttlc.png)
и наоборот(энергия закончилась, сообщаем овечке, что пора спать).
![](https://habrastorage.org/webt/z4/kr/zq/z4krzqprxf9uk-8vvrtpp9nigok.png)
Проблемы возникают тогда, когда не успевают обновиться переменные, вычисляемые в одном из скриптов в методе update, ведь сообщения вне очереди. Но это ничего, главное привыкнуть.
Еще один нюанс — это адреса объектов. По сути это строки, однако, на мобильных платформах строки отключаются для экономии ресурсов(где, блин, это написано!?), вместо них идет хэш. Вроде ничего, да, но если ваша игровая логика шлет сообщения через отредактированые строки, то жди беды, почтовая служба не вручит письмо адресату. Поэтому, резервируйте адреса в переменные через метод msg.url() и не трогайте. Собственно, это мелочи, но первое время страшно отнимает драгоценное время. Например, для того, чтобы выяснить, стоит ли овечка на земле, нужно добавить дополнительное кинематическое тело чуть ниже ног, и у него спрашивать, есть ли вхождение в землю.
Dragon bones вместо Spine
Без проблем, используем дракона, ведь позвончик очень даже платный. Но, как всегда есть но. У позвоночника есть функция — скин, у дракона ее нет. Как решить? Руками. Открывает json из spine и смотрим где есть skin, анализируем, копируем, вставляем в наш json от дракона и правим как надо, вот и все. Скрипт на питоне экономит вам приличную сумму. Что касается скриптов, так это вообще сказка. Так как все файлы текстовые, то и редактирование можно автоматизировать. Вот например, собрал в редакторе уровень используя общий атлас с большим количеством текстур(более 300), однако использовал не все, только около 100. Чтобы руками не перебирать, я «наПитонил» оптимизатор атласа — все, что используется в коллекции, остается, остальное выкидывается. Очень удобно, когда можно что-то автоматизировать.
Share и Admob
Кнопку Share можно найти в ассетах и пристроить за пару минут. Осторожно с ассетами! Ассет Admob например, существенно замедляет билд, а также вообще не дается, когда нет подключения к сети. В случае с Admob(Не буду писать как его встроить, все есть в документации на гитхабе), еще приходится делать проверку на платформу, иначе билд на окнах выдаст ошибку. Поэтому, при разработке, отложите рекламу и прочие ассеты напоследок, т.к. билд у Дефолда без них очень быстрый. Что касается локализации, можно создать себе вот такой словарик:
dict["ru"]["lang"]="Я русский"
dict["en"]["lang"]="Ya ne russkiy"
и менять текст при инициализации, обращаясь следующим образом:
language=sys.get_sys_info().language
label.set_text("game:/go#label",dict[language]["lang"]
Вот, в принципе и все, по итогу Дефолд очень крутой(когда в нем разобрался), поэтому всем советую. Кому интересно, что у меня из под него вышло, смотрите трейлер здесь(не ради пиара, моя ЦА — дети).