Defold. Некоторые сложности и их решения. (UPD. 02.01.2021)

Читая предыдущий пост о Defold может сложится впечатление, что движок идеален и просто “серебряная пуля”. Да, так и есть ^___^.  В этот раз я расскажу о сложностях, с которыми  столкнулся и как они решаются.

render_script

– это lua скрипт, в котором весь render pipeline, как на ладони. Там можно менять размер и пропорции видимой области,  порядок отрисовки, проекцию камеры и многое другое. Подробности о том, что же умеет render_script и как подменить встроенный скрипт своим, можно прочитать в официальной документации, а здесь про конкретные задачи и их решения.

Game Scale

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

-- при старте уровня отправляю сообщение в render_script
msg.post("@render:", "level_start", {level_width = lvl_width, level_height = lvl_height})
-- получаю сообщение и задаю размеры области для отрисовки
function on_message(self, message_id, message)
    ...
    elseif message_id == hash("level_start") then
       	original_width = message.level_width
       	original_height = message.level_height
    end
end

Кроме того, есть готовые модули камер, помогающие решить такого рода задачи:
https://defold.com/assets/orthographic/

https://defold.com/assets/rendercam/

Render predicates

– определяет порядок отрисовки по тэгу в материале. Подробности со всеми премудростями в документации, а вот где это пригодилось  в проекте.

GUI в движке Defold – это отдельный компонент со своими типами объектов – нодами. И этот компонент отрисовывается поверх всего. Но иногда хочется сменить порядок отрисовки gui или использовать его не по назначению.

Для начала создадим копию материала, что используется для GUI. По умолчанию он находится в builtins/materials/gui.material . Зададим ему tag, у меня это my_gui. Далее идем в наш render_script и создадим там новый предикат. А после отрисуем его там, где положено. Выглядит это примерно вот так:

--в вашем render_script
function init(self)
  ...
  --создаем наш предикат
  self.my_gui_pred = render.predicate({"my_gui"})
  ...
end
function update(self)
   ...
    render.draw(self.gui_pred)
    render.draw(self.text_pred)
   ...
    --отрисовываем наш предикат там, где нужно
    render.draw(self.my_gui_pred)
   ...
end

Теперь у нас есть материал, который мы можем задавать для всех gui которые должны отрисоваться в нужном нам порядке.

Materials 

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

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

ful



Как видно, левая картинка намного четче, чем правая (несмотря на то, что изображение уже пережато несколько раз). Так вот, левый скриншот сделан при скейле 1, а правый при скейле 0.5. В принципе, при любом скейле отличным от 1 картинка заметно “плывет”. Из опыта работы на флэш родилась мысль об использовании нескольких наборов текстур, и все знакомые Flash разработчики пропагандировали именно этот подход. Но опыт работы с Unity подсказывал, что копать нужно в сторону сжатия текстур. После тщательного изучения форума и документации  таки решил спросить сообщество как с этим быть, ответили мне моменталтно и решение оказалось крайне простым.

2016-06-27_22-12-12

В настройках материала мы можем создать sampler и задать его тип фильтрации. В моем случае, необходимо было задать FILTER_MODE_LINEAR. Но здесь есть один нюанс, который я упустил. Сэмплер – это тип переменной шейдера. Создавая его в материале, мы просто добавляем параметры для переменной, которую укажем в поле name. А переменная эта задается в шейдере. Например, этот скриншот для материала sprite использует шейдер sprite.fp :

varying mediump vec4 position;
varying mediump vec2 var_texcoord0;

uniform lowp sampler2D DIFFUSE_TEXTURE;
uniform lowp vec4 tint;

void main()
{
    // Pre-multiply alpha since all runtime textures already are
    lowp vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);
    gl_FragColor = texture2D(DIFFUSE_TEXTURE, var_texcoord0.xy) * tint_pm;
}

Именно DIFFUSE_TEXTURE мы и должны указать в качестве значения поля name в samplers. А вот шейдер gui.fp используемый в материале gui:

varying mediump vec4 position;
varying mediump vec2 var_texcoord0;
varying lowp vec4 var_color;

uniform lowp sampler2D texture;

void main()
{
    lowp vec4 tex = texture2D(texture, var_texcoord0.xy);
    gl_FragColor = tex * var_color;
}

Здесь переменная сэмплер имеет имя texture, поэтому, не забываем смотреть на код шейдера и в параметрах материала писать правильное имя переменной сэмплера.

Теперь у нас четкая картинка с любым скейлом 🙂

result

Другое.

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

Конечно, есть еще всякие мелочи:

  • встроенный твинер не умеет работать с пользовательскими векторами (но умеет с некоторыми свойствами объектов, что являются векторами) – уже создана таска. Штука совершенно не критичная и легко обходится.
  • в редакторе не хватает возможности кастомизации.

Я понимаю, что все эти мелочи могут раздражать (хотя меня не раздражают, их не так много), но они с лихвой перекрываются плюсами движка.

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

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.