Пользователи играют в игры на устройствах с разными размерами экранов, так что при разработке игр очень важен адаптивный пользовательский интерфейс (User Interface, UI). Продуманный адаптивный UI обеспечивает идеальный внешний вид на любом экране. В Godot Engine есть широкий выбор настроек и нодов, упрощающих создание адаптивного UI.
В этом туториале вы научитесь пользоваться такими настройками и нодами. К концу туториала вы будете знать, как сделать UI адаптивным, и реализуете переключение между полноэкранным и оконным режимами.
Приступаем к работе
В туториале используется самая новая версия Godot 4. Её можно скачать с веб-сайта Godot.
Материалы для туториала можно скачать по ссылке, а затем распаковать файл zip в любое место.
В материалах есть два проекта:
Starter: этот проект содержит UI без адаптивности. Позже мы превратим его в адаптивный UI.
Final: готовая версия адаптивного UI, которую можно сразу запустить.
Кроме того, для понимания этого туториала требуются базовые знания Godot Engine и GDScript. Освоить их вам помогут следующие туториалы:
Обзор файлов
Импортируйте и откройте проект Starter в Godot Project Manager. Или дважды нажмите на project.godot в папке starter.
В панели FileSystem находятся следующие файлы и папки:
-
scenes: папка с файлами сцены.
game_map.tscn: тайловая карта.
player.tscn: персонаж Player.
main_scene.tscn: основная сцена.
-
scripts: скрипты, привязанные к сценам.
camera_follow.gd: скрипт Camera2D. Он заставляет камеру следовать за нодом Player.
player.gd: скрипт, позволяющий управлять персонажем Player.
main_scene.tscn: скрипт для поддержки основной сцены.
sprites: папка с графикой.
Основная сцена
Дважды нажмём на main/scene.tscn в панели FileSystem, чтобы открыть сцену MainScene и переключимся на экран 2D. Мы увидим следующее:
Теперь запустим сцену, нажав F5. Как видите, игра имеет простой интерфейс. Можно управлять движением персонажа по карте с помощью кнопок со стрелками.
Версия starter не поддерживает обработку адаптивности, поэтому при изменении размера окна вы можете заметить проблемы с UI.
Наша цель — превратить простой UI в адаптивный, который будет выглядеть красиво при всех разрешениях окна.
Зачем создавать адаптивный UI
Адаптивный UI важен, потому что пользователи могут играть на разных устройствах с разным разрешением экрана.
Вот примеры популярных разрешений экрана на разных платформах:
Десктопы: 1920×1080, 1366×768, 1280×1024, 1024×768
Мобильные: 667×375, 736×414, 800×360, 844×390
Планшеты: 2732×2048, 1024×768, 601×962
Плохая адаптивность UI может вызывать проблемы у игроков и мешать игровому процессу.
При некачественном адаптивном UI могут возникать следующие проблемы:
Элементы UI могут оказываться слишком маленькими.
Элементы UI могут располагаться за пределами экрана.
Элементы UI не заполняют нужное пространство до конца или накладываются друг на друга.
Адаптивный UI предназначен для обеспечения целостного игрового процесса на любых экранах.
Основы дизайна пользовательского интерфейса
Перед созданием адаптивного UI важно ознакомиться с основами дизайна UI.
Ниже мы поговорим о разрешении дизайна, соотношении сторон и ориентации.
Разрешение дизайна
Проектирование пользовательского интерфейса можно сравнить с размещением прямоугольников на холсте.
Эти прямоугольники представляют собой элементы UI наподобие меток и кнопок. Холст — это наш игровой экран. Разрешение дизайна — это целевые размеры холста, помогающие определить ширину и высоту элементов UI для соответствия требований к дизайну.
Соотношение сторон
Соотношение сторон (Aspect ratio) — ещё один важный фактор в дизайне UI. Он определяется делением ширины экрана на его высоту. Сравнивая соотношение сторон вашего дизайна и экрана, вы будете знать, шире ли экран, уже или такой же.
Вот сравнение различных размеров экранов и разрешений дизайна:
Ориентация
Последний важный фактор — это ориентация.
Существует два типа ориентации:
Портретная (Portrait): ширина экрана меньше, чем высота.
Альбомная (Landscape): ширина экрана больше, чем высота.
Очень важно правильно выбрать ориентацию на ранних этапах разработки. Она существенно влияет на дизайн пользовательского интерфейса. Например, в играх, в первую очередь рассчитанных на десктоп, обычно используется альбомная ориентация, а в мобильных играх чаще всего применяется портретная.
Разбираемся со способами обработки адаптивного UI
Существует два основных подхода к созданию адаптивного UI.
При первом мы сохраняем соотношение сторон и масштабируем все элементы UI под новое разрешение. Все оставшиеся области не будут рендериться и отображаются, как чёрный фон.
При втором подходе мы изменяем соотношение сторон и выполняем масштабирование под соотношение сторон экрана. При таком подходе нужно учитывать два сценария:
Определять привязку элементов UI.
Обрабатывать искажения элементов графического UI.
Далее мы узнаем, как применять эти два подхода в Godot.
Первый подход: сохранение соотношения сторон
Откройте Project Settings и выберите Display > Window setting. Затем перейдите в раздел Stretch section, выберите для свойства Mode значение canvas_items, а для свойства Aspect значение keep.
При перезапуске игры вы заметите, что UI реагирует на изменение размеров экрана.
Результат будет таким:
Теперь UI не приклеен к левому верхнему углу экрана.
Однако если долго двигаться в одном направлении, то персонаж игрока выйдет за пределы видимости.
Давайте решим эту проблему.
Привязываем экран к перемещениям игрока
Чтобы исправить это, добавим нод Camera со скриптом перемещения камеры.
Сначала создадим нод Camera2D как дочерний элемент корневого нода MainScene. Нод Camera2D будет привязывать окно просмотра к области вокруг позиции камеры.
Дальше привяжем скрипт следования камеры camera_follow.gd к ноду Camera2D. Выберем нод Camera2D и найдём его свойство Script.
Теперь нажмём на кнопку Quick Load, выберем скрипт camera_follow.gd и нажмём на Open. Этот скрипт центрирует игрока на экране и выравнивает позицию камеры с его позицией.
Настроив всё это, запустите игру, чтобы понаблюдать за изменениями.
Камера теперь следует за игроком, не позволяя ему покинуть видимую область.
Но можно заметить, что экранные панели интерфейса (HUD) позиционируется неправильно. Панели уровня и опыта не центрированы. Решим эту проблему.
Используем CanvasLayer для создания панелей интерфейса
Чтобы камера не влияла на UI, используем нод CanvasLayer, в котором будут содержаться элементы UI.
Сначала выберем нод MainScene и создадим нод CanvasLayer в качестве его дочернего элемента.
После добавления CanvasLayer перетащим в него нод HUD в иерархии сцены.
Соберём и запустим проект, чтобы увидеть изменения.
При создании UI рекомендую использовать для хранения элементов UI нод CanvasLayer.
Мы узнали простой способ создания адаптивного UI. Его можно использовать для прототипирования и геймджемов. К тому же он малозатратен с точки зрения ресурсов.
Разбираемся в параметре Stretch
Прежде чем приступить ко второму подходу, необходимо разобраться в настройках растягивания.
Настройки растягивания (stretch) управляют растягиванием отображаемого контента под разные экраны.
В разделе Stretch есть четыре параметра.
Mode: управляет способом растяжения контента под размеры экрана.
Aspect: управляет сохранением соотношения сторон контента и расширением контента игры.
Scale: добавляет контенту дополнительный масштаб.
Scale Mode: управляет целочисленностью коэффициента масштабирования.
Изучаем настройки Stretch Mode
От настройки Stretch Mode зависит, растягивается ли контент при смене разрешения.
Stretch Mode = Disabled
При таком режиме контент не масштабируется до разрешения экрана.
Вот демонстрация:
Эта опция обычно используется при разработке инструментов или приложений.
Stretch Mode = CanvasItem (2D) или Viewport:
Эти два режима масштабируют контент в зависимости от разрешения дизайна и экрана.
Режим Viewport масштабирует всё, в том числе и 3D-ноды. Режим CanvasItem масштабирует только 2D-ноды.
Демонстрация:
Изучаем настройки Stretch Aspect
Если для Stretch Mode выбрана настройка CanvasItem или Viewport, можно использовать параметр Stretch Aspect.
Stretch Aspect управляет соотношением сторон и масштабированием при смене разрешения.
Вот, что означает каждая опция:
Ignore: эта опция не учитывает соотношение сторон при смене разрешения. Контент масштабируется так, чтобы полностью заполнить экран и выглядит искажённым. Поэтому использовать эту опцию не рекомендуется.
Keep: эта опция сохраняет соотношение сторон контента при смене разрешения. Контент центрируется на экране, а остальные области остаются чёрными.
Keep Width: UI сохраняет свою ширину, а высота растягивается для заполнения экрана.
Keep Height: UI сохраняет свою высоту, а ширина растягивается для заполнения экрана.
Expand: UI масштабируется и растягивается, чтобы полностью заполнить экран.
При использовании режимов Keep Width, Keep Height и Expand соотношение сторон и размер UI меняется. Поэтому при выборе этих режимов нужно задать их Anchor, чтобы они адаптировались к новому соотношению сторон.
Разбираемся с Anchor
При создании адаптивного UI важны якоря (Anchor). Они сообщают ноду UI, как менять размер или позицию при изменении размера его родительского нода.
Это поведение можно задать при помощи свойства Anchor Preset в инспекторе нода Control.
Есть два основных вида якорей:
Якоря позиции: они определяют, как ноды UI будут выравниваться относительно их родительских нодов.
Якоря заполнения: определяют, как ноды UI будут заполнять родительские ноды.
Якоря позиции
Существует десять якорей позиции (position anchor). Они основаны на комбинировании верха, низа, левой и правой стороны, а также центра родительского нода. При использовании такого якоря нод UI не масштабируется.
Вот демонстрация якорей позиции:
Якоря заполнения
Есть семь якорей заполнения (fill anchor). Они основаны на заполнении верха, низа, левой и правой части, а также центра родительского нода. При использовании этого вида якоря нод UI масштабируется.
Вот демонстрация якорей заполнения:
Top Wide, Bottom Wide, Left Wide и Right Wide:
Center Wide, H Center Wide и Full Rect:
Второй подход: заполнение окна контентом
Теперь мы рассмотрим второй подход: расширение окна просмотра и UI для заполнения экрана.
Чтобы использовать его, откройте Project Settings и выберите Display > Window. Измените свойство Aspect на Expand.
При запуске игры вы увидите, что карта и UI занимают экран целиком, без чёрной границы по краям.
Усовершенствуем интерфейс при помощи якорей
Можно заметить, что при изменении размеров окна игры размер интерфейса (HUD) оказывается неправильным.
Причина в том, что для него не настроен якорь. Чтобы исправить это, настроим для HUD следующие параметры:
Anchor Preset: Top Wide
Size: X = 1024, Y = 100
Запустим игру заново. Слой HUD соответствует экрану даже при изменении размеров. Однако мы видим, что информация об уровне и опыте по-прежнему не выровнена.
Это тоже проблема якорей, необходимо изменить Anchor Preset нодов.
Вот конфигурации LevelLabel и ExpLabel.
LevelLabel:
Layout Mode: Anchors
Anchor Preset: Center Top
Position: X = 312, Y = 0
ExpLabel:
Layout Mode: Anchors
Anchor Preset: Center Top
Position: X = 312, Y = 50
Показанные выше настройки центрируют LevelLabel и ExpLabel при изменении размеров окна.
Запустив игру, мы увидим корректный адаптивный UI.
Украшаем HUD с помощью NinePatchRect
Мы уже научились делать готовый UI адаптивным. Теперь нам нужно узнать, как создавать новый адаптивный UI.
Сначала нужно дополнить фон HUD растягиваемой текстурой, использующей нод NinePatchRect. Это специальный текстурный нод, растягивающий не всё изображение, а только часть текстуры.
Для начала создадим в HUD новый нод типа NinePatchRect и поместим его над LevelLabel. Затем переименуем этот нод в HUDBackground.
Далее перетащим изображение panel-border.png из панели File System в свойство Texture нода HUDBackground. После этого зададим свойствам якорей HUDBackground следующие значения:
Layout Mode: Anchors
Anchor Presets: Full Rect
Эти настройки заставят нод растягиваться при изменении размеров окна.
Далее зададим свойствам Patch Margin нода HUDBackground следующие значения:
Left: 48 px
Top: 48 px
Right: 48 px
Bottom: 48 px
Эти настройки позволяют текстуре растягиваться только в центральной части.
Запустите игру. Вы увидите, что фон выглядит ровным, хотя размер окна меняется.
Создание пользовательского интерфейса при помощи контейнеров
Ещё один инструмент для создания адаптивного UI — это нод Container.
В Godot ноды Container управляют размером и позицией своих дочерних нодов. Существуют разные контейнеры под разные структуры.
Когда нод Control становится дочерним нодом Container, его режим Layout переключается на Container и не может быть изменён. Кроме того, пользователь не может менять значения в разделе Transform, например, позицию и размер, потому что нод Container сам вычисляет позицию и размер своих дочерних нодов.
Создаём панель здоровья и денег при помощи GridContainer
Теперь мы воспользуемся нодом GridContainer для создания панели Health and Money.
Этот контейнер выстраивает свои дочерние компоненты в сетку. Его можно использовать для выравнивания информации о здоровье и деньгах в два столбца. Первый столбец будет содержать иконки, а во втором будут метки.
Давайте создадим контейнер.
Сначала создадим нод GridContainer внутри HUD и переименуем его в HealthMoneyPanel. Затем зададим следующие свойства нода:
Columns: 2
Size: X = 200, Y = 80
Layout Mode: Anchors
Anchor Preset: Center Left
Position: X = 40, Y = 10
Вы не увидите никаких изменений, потому что дочерних элементов пока нет.
Добавляем информацию о здоровье
Далее добавим строку информации о здоровье.
Создадим нод TextureRect внутри HealthMoneyPanel и назовём его HealthIcon. Перетащим изображение life.png на свойство Texture и выберем для Expand Mode значение Fit Width.
Теперь создадим нод Label внутри HealthMoneyPanel и назовём его HealthLabel. Разместим этот нод под HealthIcon. Зададим свойству Text значение 100
и сделаем Font Size (в Control > Theme Overrides) равным 30
.
Добавляем информацию о деньгах
Теперь добавим строку информации о деньгах.
Сначала дублируем нод HealthIcon и переименуем его в MoneyIcon. Затем разместим этот нод под HealthLabel. Далее перетащим изображение coin.png на его свойство Texture.
Дублируем нод HealthLabel. Переименуем его в MoneyLabel, а затем разместим этот нод под MoneyIcon. Присвоим свойству Text значение 1000
.
Теперь можно увидеть структуру, переключившись в 2D view.
Запустите игру и обратите внимание на изменения. Информация о здоровье и деньгах будет выровнена даже при изменении размера окна.
Вот демо новой структуры интерфейса:
Переключение между полноэкранным и оконным режимом
Теперь мы научимся переключаться игру из полноэкранного режима в оконный и наоборот. Эта функция часто используется в десктопных играх, потому что некоторые игроки предпочитают играть в полноэкранном режиме, а другие — в оконном.
Начнём мы с добавления двух кнопок, включающих режим Fullscreen и Window.
Создаём элементы управления полноэкранным и оконным режимами
Для начала создадим HBoxContainer для хранения кнопок и назовём его WindowModePanel.
Сконфигурируем свойства WindowModePanel следующим образом:
Size: x=100, y=48
Layout Mode: Anchors
Anchors Preset: Center Right
Position: x=904, y=26
Theme Overrides > Constants > Separation: 5
Далее добавим TextureButton в качестве дочернего элемента WindowModePanel и переименуем его в WindowButton.
Затем сконфигурируем WindowButton. Перетащим btn-window.png на свойство Textures (Normal) и присвоим Stretch Mode значение Keep Aspect.
Дублируем нод WindowButton и переименуем копию в FullscreenButton.
Теперь настроим FullscreenButton. Перетащим btn-fullscreen.png на свойство Textures (Normal).
Добавляем логику полноэкранного и оконного режимов
Добавим кнопкам скрипт логики.
Сначала выберем нод FullscreenButton во вкладке Scene. Присвоим его свойству Script значение main_scene.gd.
Затем переключимся во вкладку Node, выберем сигнал pressed() и нажмём на кнопку Connect.
В диалоговом окне Connect Signal выберем для Receiver Method _on_fullscreen_button_pressed
и нажмём кнопку Connect.
Изменим метод _on_fullscreen_button_pressed
в скрипте main_scene.gd
следующим образом:
func _on_fullscreen_button_pressed():
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)
Этот код использует DisplayServer.window_set_mode
для переключения из оконного режима в полноэкранный.
Теперь настроим кнопку оконного режима.
Сначала выберем нод во вкладке Scene для WindowButton. Выберем для свойства Script значение main_scene.gd
.
Далее переключимся во вкладку Node. Выберем сигнал pressed() и нажмём на кнопку Connect.
В диалоговом окне Connect Signal зададим Receiver Method значение _on_window_button_pressed
и нажмём на кнопку Connect.
Далее изменим метод _on_window_button_pressed
в скрипте main_scene.gd
следующим образом:
func _on_window_button_pressed():
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
Этот код вызывает метод DisplayServer.window_set_mode
для переключения игры в оконный режим.
Запустите игру и проверьте функциональность. Теперь вы можете переключаться между полноэкранным и оконным режимами кнопками FullscreenButton и WindowButton.
Куда двигаться дальше?
Теперь вы знаете, как создавать адаптивный UI. Поэкспериментируйте с разными режимами растяжения (Stretch), чтобы подобрать подходящий вам. Также можете попробовать разные якоря и контейнеры движка Godot.
Более подробную информацию можно прочитать в статьях, написанных командой Godot:
Комментарии (7)
KawaiiSelbst
13.12.2024 08:47Удивляюсь продуктивности Ken Lee, в количестве обучающих материалов для всего на свете
de43gy
13.12.2024 08:47UI в Годо мне сложнее всего дался и не то что бы я до конца его понял. Скорее интуитивно что-то делаю и иногда это работает как планировалось...
Спасибо за статью!
И вообще очень рад что по этому движку на хабре что-то обучающее появляется!Odin41
13.12.2024 08:47На ютубе есть хороший, канал
GameCrafter, на русском) и сейчас как раз идет свежий плейлист по созданию игры. Ролики короткие, можно найти нужную тему.
А по статье, возможно я уже в этом разобрался больше чем расписано, но по мне статья слишком длинная. Много информации из разных частей. И часть изображения не прогружается.
anoneko
Годотом только и пользоваться после их фиаско с вокеизмом. Скурвились ребята.
Arkology
Ага, как запускаешь движок, сразу рекламу пихают этих ваших воков. Приходится монитор разбивать, чтобы через него не заразиться проказой.
*sarcasm mode off*
Не, ну серьезно, кем надо быть, чтобы думать, что какая-либо повесточка может влиять на такую вещь, как разрабатываемых совместно с сообществом FOSS движок (где количество основных разработчиков по сравнению с количеством контрибьютеров порядка 1 к 1000 как минимум). При том, что разработчики всегда (!) были "против всего плохого и за все хорошее", вот сколько пулл реквестов из 45 тысяч было с повесткой?
---
Ну а если по теме статьи. Система GUI в Godot с их якорями - сильная штука. Не без проблем, конечно, и можно закончить с 100+ узлами на одно разрабатываемое окно интерфейса (и запутаться потом в списке узлов), но проблемы эти решаемые и преимущества системы перекрывают недостатки (хотя как всегда "могло быть и лучше").
IvanBodhidharma
Кто все эти слова только выдумывает. Зачем я опять загуглил и узнал про фаллосоцентрический цисгендеронормативный патриархат...
de43gy
ну не пользуйся, вон есть скрепно-православный редот и куча других движков с открытым кодом и без