Пользователи играют в игры на устройствах с разными размерами экранов, так что при разработке игр очень важен адаптивный пользовательский интерфейс (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. Мы увидим следующее:

Open main scene

Теперь запустим сцену, нажав F5. Как видите, игра имеет простой интерфейс. Можно управлять движением персонажа по карте с помощью кнопок со стрелками.

Версия starter не поддерживает обработку адаптивности, поэтому при изменении размера окна вы можете заметить проблемы с UI.

Resize starter project

Наша цель — превратить простой 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 не заполняют нужное пространство до конца или накладываются друг на друга.

Screen demo of the problem

Адаптивный UI предназначен для обеспечения целостного игрового процесса на любых экранах.

Основы дизайна пользовательского интерфейса

Перед созданием адаптивного UI важно ознакомиться с основами дизайна UI.

Ниже мы поговорим о разрешении дизайна, соотношении сторон и ориентации.

Разрешение дизайна

Проектирование пользовательского интерфейса можно сравнить с размещением прямоугольников на холсте.

Design 1

Эти прямоугольники представляют собой элементы UI наподобие меток и кнопок. Холст — это наш игровой экран. Разрешение дизайна — это целевые размеры холста, помогающие определить ширину и высоту элементов UI для соответствия требований к дизайну.

Соотношение сторон

Соотношение сторон (Aspect ratio) — ещё один важный фактор в дизайне UI. Он определяется делением ширины экрана на его высоту. Сравнивая соотношение сторон вашего дизайна и экрана, вы будете знать, шире ли экран, уже или такой же.

Вот сравнение различных размеров экранов и разрешений дизайна:

Compare screen size
Разрешение дизайна, более узкий и более широкий экраны

Ориентация

Последний важный фактор — это ориентация.

Существует два типа ориентации:

  • Портретная (Portrait): ширина экрана меньше, чем высота.

  • Альбомная (Landscape): ширина экрана больше, чем высота.

Compare portrait / landscape
Альбомная и портретная ориентации

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

Разбираемся со способами обработки адаптивного UI

Существует два основных подхода к созданию адаптивного UI.

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

Responsive Approach 1

При втором подходе мы изменяем соотношение сторон и выполняем масштабирование под соотношение сторон экрана. При таком подходе нужно учитывать два сценария:

  • Определять привязку элементов UI.

  • Обрабатывать искажения элементов графического UI.

Responsive Approach 2

Далее мы узнаем, как применять эти два подхода в Godot.

Первый подход: сохранение соотношения сторон

Откройте Project Settings и выберите Display > Window setting. Затем перейдите в раздел Stretch section, выберите для свойства Mode значение canvas_items, а для свойства Aspect значение keep.

Aspect Keep Setting

При перезапуске игры вы заметите, что UI реагирует на изменение размеров экрана.

Результат будет таким:

Aspect Keep Stretch setting

Теперь UI не приклеен к левому верхнему углу экрана.

Однако если долго двигаться в одном направлении, то персонаж игрока выйдет за пределы видимости.

Character move out of screen

Давайте решим эту проблему.

Привязываем экран к перемещениям игрока

Чтобы исправить это, добавим нод Camera со скриптом перемещения камеры.

Сначала создадим нод Camera2D как дочерний элемент корневого нода MainScene. Нод Camera2D будет привязывать окно просмотра к области вокруг позиции камеры.

Дальше привяжем скрипт следования камеры camera_follow.gd к ноду Camera2D. Выберем нод Camera2D и найдём его свойство Script.

Теперь нажмём на кнопку Quick Load, выберем скрипт camera_follow.gd и нажмём на Open. Этот скрипт центрирует игрока на экране и выравнивает позицию камеры с его позицией.

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

Demo of camera follow

Камера теперь следует за игроком, не позволяя ему покинуть видимую область.

Но можно заметить, что экранные панели интерфейса (HUD) позиционируется неправильно. Панели уровня и опыта не центрированы. Решим эту проблему.

Используем CanvasLayer для создания панелей интерфейса

Чтобы камера не влияла на UI, используем нод CanvasLayer, в котором будут содержаться элементы UI.

Сначала выберем нод MainScene и создадим нод CanvasLayer в качестве его дочернего элемента.

После добавления CanvasLayer перетащим в него нод HUD в иерархии сцены.

Add CanvasLayer

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

Simple responsive UI ready
Простой адаптивный UI готов

При создании UI рекомендую использовать для хранения элементов UI нод CanvasLayer.

Мы узнали простой способ создания адаптивного UI. Его можно использовать для прототипирования и геймджемов. К тому же он малозатратен с точки зрения ресурсов.

Разбираемся в параметре Stretch

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

Настройки растягивания (stretch) управляют растягиванием отображаемого контента под разные экраны.

Screen stretch setting
Screen stretch setting

В разделе Stretch есть четыре параметра.

  • Mode: управляет способом растяжения контента под размеры экрана.

  • Aspect: управляет сохранением соотношения сторон контента и расширением контента игры.

  • Scale: добавляет контенту дополнительный масштаб.

  • Scale Mode: управляет целочисленностью коэффициента масштабирования.

Изучаем настройки Stretch Mode

От настройки Stretch Mode зависит, растягивается ли контент при смене разрешения.

Stretch Mode = Disabled

При таком режиме контент не масштабируется до разрешения экрана.

Вот демонстрация:

Stretch Mode disable

Эта опция обычно используется при разработке инструментов или приложений.

Stretch Mode = CanvasItem (2D) или Viewport:

Эти два режима масштабируют контент в зависимости от разрешения дизайна и экрана.

Режим Viewport масштабирует всё, в том числе и 3D-ноды. Режим CanvasItem масштабирует только 2D-ноды.

Демонстрация:

Stretch Mode CanvasItem

Изучаем настройки Stretch Aspect

Если для Stretch Mode выбрана настройка CanvasItem или Viewport, можно использовать параметр Stretch Aspect.

Stretch Aspect управляет соотношением сторон и масштабированием при смене разрешения.

Вот, что означает каждая опция:

Ignore: эта опция не учитывает соотношение сторон при смене разрешения. Контент масштабируется так, чтобы полностью заполнить экран и выглядит искажённым. Поэтому использовать эту опцию не рекомендуется.

Stretch Aspect Ignore

Keep: эта опция сохраняет соотношение сторон контента при смене разрешения. Контент центрируется на экране, а остальные области остаются чёрными.

Stretch Aspect Keep

Keep Width: UI сохраняет свою ширину, а высота растягивается для заполнения экрана.

Stretch Aspect Keep Width

Keep Height: UI сохраняет свою высоту, а ширина растягивается для заполнения экрана.

Stretch Aspect Keep Height

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

Stretch Aspect Expand

При использовании режимов Keep WidthKeep Height и Expand соотношение сторон и размер UI меняется. Поэтому при выборе этих режимов нужно задать их Anchor, чтобы они адаптировались к новому соотношению сторон.

Разбираемся с Anchor

При создании адаптивного UI важны якоря (Anchor). Они сообщают ноду UI, как менять размер или позицию при изменении размера его родительского нода.

Это поведение можно задать при помощи свойства Anchor Preset в инспекторе нода Control.

Anchor Preset property

Есть два основных вида якорей:

  • Якоря позиции: они определяют, как ноды UI будут выравниваться относительно их родительских нодов.

  • Якоря заполнения: определяют, как ноды UI будут заполнять родительские ноды.

Якоря позиции

Существует десять якорей позиции (position anchor). Они основаны на комбинировании верха, низа, левой и правой стороны, а также центра родительского нода. При использовании такого якоря нод UI не масштабируется.

Вот демонстрация якорей позиции:

Positional Anchor

Якоря заполнения

Есть семь якорей заполнения (fill anchor). Они основаны на заполнении верха, низа, левой и правой части, а также центра родительского нода. При использовании этого вида якоря нод UI масштабируется.

Вот демонстрация якорей заполнения:

Top WideBottom WideLeft Wide и Right Wide:

Fill Anchor Side

Center WideH Center Wide и Full Rect:

Fill Center

Второй подход: заполнение окна контентом

Теперь мы рассмотрим второй подход: расширение окна просмотра и UI для заполнения экрана.

Чтобы использовать его, откройте Project Settings и выберите Display > Window. Измените свойство Aspect на Expand.

При запуске игры вы увидите, что карта и UI занимают экран целиком, без чёрной границы по краям.

Aspect Expand

Усовершенствуем интерфейс при помощи якорей

Можно заметить, что при изменении размеров окна игры размер интерфейса (HUD) оказывается неправильным.

Incorrect HUD

Причина в том, что для него не настроен якорь. Чтобы исправить это, настроим для HUD следующие параметры:

  • Anchor Preset: Top Wide

  • Size: X = 1024, Y = 100

Запустим игру заново. Слой HUD соответствует экрану даже при изменении размеров. Однако мы видим, что информация об уровне и опыте по-прежнему не выровнена.

HUD fixed

Это тоже проблема якорей, необходимо изменить 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.

Level Label fixed

Украшаем 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

Эти настройки позволяют текстуре растягиваться только в центральной части.

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

Nine Patch background ready

Создание пользовательского интерфейса при помощи контейнеров

Ещё один инструмент для создания адаптивного UI — это нод Container.

В Godot ноды Container управляют размером и позицией своих дочерних нодов. Существуют разные контейнеры под разные структуры.

Когда нод Control становится дочерним нодом Container, его режим Layout переключается на Container и не может быть изменён. Кроме того, пользователь не может менять значения в разделе Transform, например, позицию и размер, потому что нод Container сам вычисляет позицию и размер своих дочерних нодов.

Using 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.

Запустите игру и обратите внимание на изменения. Информация о здоровье и деньгах будет выровнена даже при изменении размера окна.

Вот демо новой структуры интерфейса:

Money and Health panel

Переключение между полноэкранным и оконным режимом

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

Начнём мы с добавления двух кнопок, включающих режим Fullscreen и Window.

Создаём элементы управления полноэкранным и оконным режимами

Для начала создадим HBoxContainer для хранения кнопок и назовём его WindowModePanel.

Add window mode panel instruction
Add window mode panel instruction

Сконфигурируем свойства WindowModePanel следующим образом:

  • Size: x=100, y=48

  • Layout Mode: Anchors

  • Anchors Preset: Center Right

  • Position: x=904, y=26

  • Theme Overrides > Constants > Separation: 5

Setting of windowModePanel

Далее добавим TextureButton в качестве дочернего элемента WindowModePanel и переименуем его в WindowButton.

Затем сконфигурируем WindowButton. Перетащим btn-window.png на свойство Textures (Normal) и присвоим Stretch Mode значение Keep Aspect.

Дублируем нод WindowButton и переименуем копию в FullscreenButton.

Теперь настроим FullscreenButton. Перетащим btn-fullscreen.png на свойство Textures (Normal).

Add window button instruction

Добавляем логику полноэкранного и оконного режимов

Добавим кнопкам скрипт логики.

Сначала выберем нод FullscreenButton во вкладке Scene. Присвоим его свойству Script значение main_scene.gd.

Затем переключимся во вкладку Node, выберем сигнал pressed() и нажмём на кнопку Connect.

В диалоговом окне Connect Signal выберем для Receiver Method _on_fullscreen_button_pressed и нажмём кнопку Connect.

Add full screen button connect instruction

Изменим метод _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.

Full screen toggle

Куда двигаться дальше?

Теперь вы знаете, как создавать адаптивный UI. Поэкспериментируйте с разными режимами растяжения (Stretch), чтобы подобрать подходящий вам. Также можете попробовать разные якоря и контейнеры движка Godot.


Более подробную информацию можно прочитать в статьях, написанных командой Godot:

Комментарии (7)


  1. anoneko
    13.12.2024 08:47

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


    1. Arkology
      13.12.2024 08:47

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

      *sarcasm mode off*

      Не, ну серьезно, кем надо быть, чтобы думать, что какая-либо повесточка может влиять на такую вещь, как разрабатываемых совместно с сообществом FOSS движок (где количество основных разработчиков по сравнению с количеством контрибьютеров порядка 1 к 1000 как минимум). При том, что разработчики всегда (!) были "против всего плохого и за все хорошее", вот сколько пулл реквестов из 45 тысяч было с повесткой?

      ---

      Ну а если по теме статьи. Система GUI в Godot с их якорями - сильная штука. Не без проблем, конечно, и можно закончить с 100+ узлами на одно разрабатываемое окно интерфейса (и запутаться потом в списке узлов), но проблемы эти решаемые и преимущества системы перекрывают недостатки (хотя как всегда "могло быть и лучше").


    1. IvanBodhidharma
      13.12.2024 08:47

      Кто все эти слова только выдумывает. Зачем я опять загуглил и узнал про фаллосоцентрический цисгендеронормативный патриархат...


    1. de43gy
      13.12.2024 08:47

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


  1. KawaiiSelbst
    13.12.2024 08:47

    Удивляюсь продуктивности Ken Lee, в количестве обучающих материалов для всего на свете


  1. de43gy
    13.12.2024 08:47

    UI в Годо мне сложнее всего дался и не то что бы я до конца его понял. Скорее интуитивно что-то делаю и иногда это работает как планировалось...
    Спасибо за статью!
    И вообще очень рад что по этому движку на хабре что-то обучающее появляется!


    1. Odin41
      13.12.2024 08:47

      На ютубе есть хороший, канал

      GameCrafter, на русском) и сейчас как раз идет свежий плейлист по созданию игры. Ролики короткие, можно найти нужную тему.

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