Godot
April 6, 2023

Как же геморно создавать UI в Godot

Не, оно в целом выполнимо, конечно. Но насколько же много действий в редакторе + кода это требует по сравнению с фронтендом на условном React.js

Задача

  • Перед началом игры тебе нужно выбрать персонажа
  • У некоторых скиллов есть альтернативы. Их нужно выбрать перед забегом
  • Ну и, соответственно, видеть информацию о скиллах перед началом забега тоже нужно

Велосипедим

Для начала я решил переиспользовать штуки из библиотеки, которую я набросал для описания логики скиллов и перков. В частности - DState и DCombine

  • DState - класс, которая имеет состояние + метод для его обновления + сигнал updated + метод watch, который дёргает прокинутую в него функцию при обновлении стейта
  • DCombine - берёт пачку стейтов и собирает из них новый DState, значение которого вычисляется из прокинутой функции
    • Также сделал шортхэнды DCombine.Shape({ "Foo": foo }) и DCombine.List([foo, bar])

Код невероятно вербозный относительно задачи, которая стоит, но я хз как упростить в рамках GDScript.

На JS это было бы в 2 раза короче засчёт таких фич как:

  • Деструктуризация (const [a, b] = array)
  • Поддержка произвольного количества аргументов у функций
  • Отсутствие багнутого приведения типов в массивах, с которым Array[PackedScene] нельзя превратить в Array[ActionV2] с помощью array.map(func (scene): return scene.instantiate())
  • Возможность обратиться к несуществующему айтему массива (например, если его длина меньше индекса) и просто получить пустоту вместо крашей.
    • Это вообще странное решение кста. Пришлось написать хэлпер, чтобы не писать каждый раз array[idx] if array.size() > idx else null

В общем на JS это выглядело бы как-то так

Энивей, едем дальше


Поскольку Godot не оперирует реактивными сущностями, синхронизировать обновления придётся руками. Причём в обоих направлениях

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

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


В мире фронтенда всё гораздо проще - описал темплейт и данные, которые в него будут вставлены, и всё синхронизируется. По клику на кнопку обновил стейт - автоматом всё обновилось, не надо ничего руками привязывать.

Здесь, к сожалению, придётся делать всё руками. По крайней мере, если не собирать какую-то UI-библиотеку, которая сама занималась бы синхронизацией реактивных штук, но это, опять-таки, нужно изобрести.

Из похожего наткнулся на такую штуку, но она в экспериментальном состоянии + вышла в 2021м году и наверняка не работает в 4-й версии Godot.

Энивей...


Сама сцена

В целом Godot поставляет из коробки набор элементов UI. Среди них сетки с выравниванием, кнопки, текстовые поля, выбор элементов из списка итд.

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

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

То есть юзать вот эти штуки:

А не захардкоженные координаты

Так будет проще менять размер тех или иных блоков - не придётся каждый кусочек руками ресайзить и выравнивать, достаточно будет просто подвигать сам контейнер

Также заворачивайте весь интерфейс в Panel/PanelContainer, чтобы все эти штуки с позиционированием работали с самого "рута"

В общем, сцена с выбором персонажа выглядит вот так

Тут, к счастью, всё довольно просто.


Что получилось в итоге

Переключаем персонажей сверху, выбираем скиллы во вкладке "Select Skills", подробное описание во вкладке "Description"

Жесть спойлер новых абилок погчамп

C UX-овой точки зрения интерфейс, очевидно, такой себе - в идеале ты должен видеть, что выбираешь, сразу, а не идти за этим в другую вкладку

Но пока оставлю так. Функционально работает, а причёсывать буду позже, когда для интерфейса появится и графика. Историю с отображением инфы на этапе выбора думаю пофиксить обычными тултипами как в Risk of Rain 2


В целом, если в игре будет много логики, связанной с UI, то, вероятно, попробую запилить какие-нибудь примитивы, чтобы было проще жить, и не приходилось руками прописывать синхронизацию

Но пока как-то так