[Перевод] Туториал по Unreal Engine. Часть 4: UI

image


Разработчики видеоигр используют графику и текст для отображения необходимой информации, например, здоровья или очков. Это называется интерфейсом пользователя (user interface, UI).

UI в Unreal Engine 4 создаётся с помощью Unreal Motion Graphics (UMG). UMG позволяет удобно выстраивать UI, перетаскивая элементы UI, такие как кнопки и текстовые метки.

В этой части туториала вы научитесь следующему:

  • Создавать HUD-дисплей, на котором отображается счётчик и таймер
  • Отображать HUD на экране
  • Обновлять счётчик и таймер, чтобы отображать значения переменных


Стоит учесть, что мы будем использовать в этой части Blueprints. Если вам нужно освежить свои знания, то перечитайте часть туториала, посвящённую Blueprints.

Примечание: эта статья является одной из восьми частей туториала по Unreal Engine:

  • Часть 1: Знакомство с движком
  • Часть 2: Blueprints
  • Часть 3: Материалы
  • Часть 4: UI
  • Часть 5: Как создать простую игру
  • Часть 6: Анимация
  • Часть 7: Звук
  • Часть 8: Системы частиц


Приступаем к работе


Скачайте заготовку проекта и распакуйте её. Перейдите в папку проекта и откройте GeometryCatcher.uproject.

Примечание: если откроется окно, сообщающее, что проект создан в более ранней версии Unreal editor, то всё в порядке (движок часто обновляется). Можно или выбрать опцию создания копии, или опцию преобразования самого проекта.

Нажмите на Play, чтобы начать управлять белым кубом и попробуйте ловить падающие фигуры. Куб можно перемещать горизонтально с помощью мыши. Через десять секунд фигуры перестают создаваться.

927228bbe6c73c7d0d3745581c8d5896.gif


Первое, что нужно сделать — создать HUD-дисплей, отображающий две вещи:

  • Счётчик, отслеживающий количество собранных игроком фигур
  • Таймер, отображающий количество секунд, оставшихся до завершения создания фигур


Чтобы создать всё это, нам необходимы виджеты.

О виджетах


Виджет (widget) — это элемент UI, предоставляющий UI визуальные функции. Например, виджет Button предоставляет объект, который пользователь может видеть и нажимать на него.

573c22c05ce675c82a7a936a525f629d.gif


Сам виджет необязательно должен быть видимым. Например, виджет Grid Panel равномерно разделяет своё пространство между его содержимым. Пользователь не может увидеть Grid Panel, но видит его воздействие.

e9d4e2c8c543328ad0b3fe94c12fb276.jpg


Кроме того, виджеты могут содержать другие виджеты. Вот пример виджета, содержащего виджет Text (метка Name) и виджет Text Box:

eb331d0bfe2460196464a2ffc3577218.jpg


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

500f8970d2fcbcf34c361ae8664f3ef6.jpg


Итак, мы узнали, что такое виджеты. Теперь можно создать виджет для HUD.

Создание виджета


Перейдите в Content Browser и найдите папку UI. Нажмите на кнопку Add New и выберите User Interface\Widget Blueprint. Переименуйте новый ассет в WBP_HUD.

d908d3aac93c4e32bbc37eaef4f5ebc5.jpg


Дважды нажмите на WBP_HUD, чтобы открыть его в UMG UI Designer.

UMG UI Designer


UMG UI Designer состоит из семи основных элементов:

7f37d39835a10c451f689c134afa1be1.jpg
  1. Designer: в этой области представлено визуальное отображение виджета. Перемещаться по ней можно зажав правую клавишу мыши и двигая мышью. Масштабирование выполняется прокруткой колёсика мыши.
  2. Details: здесь отображаются свойства выбранного виджета
  3. Palette: список всех виджетов, которые можно использовать. Все созданные пользователем виджеты тоже появляются здесь.
  4. Hierarchy: список всех уже используемых виджетов
  5. Animations: некоторые свойства виджетов могут иметь анимацию, например, расположение и размер. В этой панели перечислены все анимации.
  6. Timeline: при выборе анимации на этой панели показываются анимированные свойства и ключевые кадры
  7. Editor Mode: здесь можно переключаться между режимами Designer и Graph. Режим Graph почти аналогичен Event Graph у Blueprint.


Создание виджета Text


Виджеты Text отлично подходят для отображения числовой информации, например, счётчика и таймера.

Перейдите в панель Palette и найдите виджет Text. Добавьте виджет, перетащив его мышью в панель Designer.

49784b000214cfcac4b93476d91f4956.gif


Содержание текста нас пока не интересует, мы изменим его позже.

Переименуйте виджет в CounterText. Это можно сделать, выбрав виджет Text и перейдя в панель Details. Введите CounterText в текстовое поле в верхней части.

a6c0e0ba2a41a7eb06c191b7d7b08187.jpg


Виджеты можно двигать, перетаскивая их левой клавишей мыши в панели Designer.

34e691e7b67b8f90567379e83ff32281.gif


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

cad89deae7f401b5571eed5a99e4c180.gif


Или же можно задать положение и размер изменением значений в панели Details. Задайте следующие свойства и значения для CounterText:

  • Position X: 200
  • Position Y: 50
  • Size X: 500
  • Size Y: 100


dd681d6448891f1d1cbf6b5e56546420.jpg


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

d67e810441b25a6da344907fe1dab87a.jpg


Можно увеличить размер шрифта, перейдя в панель Details и к разделу Appearance. Справа от свойства Font есть текстовое поле для задания размера шрифта.

42920bc5fab122c29bfcf076bfcd24d3.jpg


Введите размер 68.

f02006958f7e3c02a9d7459c618d6043.jpg


Давайте сделаем счётчик красивее, добавив к нему значок.

Создание виджета Image


Виджеты Image — это простой способ отображения графики в UI, например, значков.

Создайте виджет Image и назовите его CounterIcon. Задайте Position X значение 75, а Position Y — значение 50. Виджет разместится рядом с CounterText.

85c4715972f313de7d324b03230b51c1.jpg


Чтобы задать изображение, перейдите в панель Details и зайдите в раздел Appearance. Разверните свойство Brush, а затем нажмите на раскрывающемся списке рядом с Image. Выберите T_Counter.

4a2b3e81cd3fd03915a1f0fa72a61eef.jpg


Изображение будет выглядеть растянутым, потому что размер виджета отличается от размера изображения.

d12e61d5b5677399dc2b3e6e3e3ff830.jpg


Вместо изменения размера виджета мы можем использовать опцию Size To Content. Эта опция автоматически изменяет размер виджета под размер его содержимого.

Находясь в панели Details, перейдите в раздел Slot (Canvas Panel Slot). Поставьте флажок рядом с Size To Content.

4aa683b5cc04343c9522514bbf82cb9b.jpg


Виджет сам подгонит свой размер под изображение.

6f1c893fe7ed930c476c8bfcdf05e7b3.jpg


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

Привязки


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

В примере ниже каждое изображение привязано к одной точке (к ближайшему углу).

ea4348409f21996d2410d1951134d414.gif


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

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

В примере ниже индикатор привязан к верхнему левому и верхнему правому углам.

ad459f8a828b9dd68f029b83a13c731c.gif


Вертикально индикатор перемещается, но не меняет размера, потому что на оси Y есть только одна привязка (сверху). Однако горизонтально индикатор меняет размер, потому что имеет две точки привязки по оси X.

Anchor Medallion отображает расположение привязки. Он появляется при выборе виджета.

61d1c19deacdab9a8014404708ad837e.jpg


Привязки CounterText и CounterIcon уже находятся в нужном месте, поэтому задавать их не нужно.

Теперь мы создадим ещё по одному виджету Text и Image для таймера. Однако на этот раз мы поместим их справа.

Создание таймера


Создайте виджет Text и назовите его TimerText. Задайте следующие свойства:

  • Position X: 1225
  • Position Y: 50
  • Size X: 500
  • Size Y: 100
  • Font Size: 68
  • Justification: Align Text Right (это выровняет текст по правой стороне виджета)


50592d4b65c4ff938b2f95fc8a4632e6.jpg


Теперь мы хотим задать привязку в правом верхнем углу. Это можно сделать, перетащив мышью круг в Anchor Medallion. Переместите Anchor Medallion в правый верхний угол.

f74cda276c00b4c501045020cf313518.gif


Заметьте, как обновляется положение относительно привязки.

ffb1cce6c0fe134cb8e84359ba20a2f2.jpg


Создайте виджет Image и назовите его TimerIcon. Задайте следующие свойства:

  • Position X: 1750
  • Position Y: 50
  • Size To Content: Checked
  • Brush\Image: T_Timer


9ac1e03b9a67120296a9a5b24fa3d925.jpg


Вместо задания привязки с помощью Anchor Medallion, можно воспользоваться предустановленными значениями. Перейдите в панель Details и нажмите на раскрывающийся список рядом с Anchors, чтобы открыть предустановленные значения. Выберите третью схему (с квадратом в правом верхнем углу).

bd95193d4e7f6cb8faf1fab5201f3d16.jpg


Создание схемы UI на этом завершено. Можно убедиться в том, что привязки работают, эмулируя различные размеры экрана. Перейдите в панель Designer и нажмите на раскрывающийся список Screen Size.

3647b98ce947e25b3897b97c429c9377.jpg


Выбор опции изменит размер WBP_HUD для соответствия опции. Ниже показано, как HUD будет выглядеть на iPad Air. Заметьте, что виджеты стали ближе друг к другу.

c5032326177ec93a4845184f807d221d.jpg


В следующем разделе мы узнаем, как отображать виджет WBP_HUD.

Отображение HUD


Нажмите на Compile, а затем вернитесь в основной редактор. Перейдите в папку Blueprints и дважды щёлкните на BP_GameManager, чтобы открыть его.

HUD должен становиться видимым после запуска игры. Для этого можно использовать нод Event BeginPlay.

Найдите нод Event BeginPlay и добавьте нод Create Widget в конец цепочки нодов. Этот нод создаёт экземпляр указанного виджета.

454ee25f07bc7acbd02966b3abc248dc.jpg


Нажмите на раскрывающийся список рядом с Class и выберите WBP_HUD.

03c5f8fc963ef9fc3c8fa2a11c46ce6d.jpg


Чтобы отобразить HUD, необходимо использовать нод Add to Viewport. Перетащите левой клавишей мыши контакт Return Value нода Create Widget. Отпустите левую клавишу мыши в пустом пространстве, чтобы открыть контекстное меню. Добавьте нод Add to Viewport.

756d5c9c3ec8d30d3457bbdceb5d7a5b.jpg


Давайте разберёмся с порядком событий:

  1. Когда Unreal спаунит BP_GameManager, выполняются функции Restart и SetUpCamera. Эти функции настраивают несколько переменных и камеру. Если вы не знаете, что такое функция, то не волнуйтесь, скоро мы их рассмотрим.
  2. Нод Create Widget создаёт экземпляр WBP_HUD
  3. Нод Add to Viewport отображает WBP_HUD


Нажмите на Compile и вернитесь в основной редактор. Нажмите на Play, чтобы запустить игру с новым HUD.

40875951e13423184ab8dd1181d0e053.gif


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

1a26b43bb810cac275edb9d1dbdad9b7.jpg


Чтобы использовать эти переменные. нам нужен способ получения доступа к BP_GameManager из WBP_HUD. Для этого можно применить переменную-ссылку.

Переменные-ссылки


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

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

20f0a1a86757539367af3dee5af4a70c.jpg


А теперь представьте, что у нас сотня коробок, но мяч есть только в одной. Нам нужно будет проверять каждую коробку, пока мы не найдём коробку с мячом.

d3dee9b722b6b721a39b80c1b0ab44d4.jpg


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

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

bcd6687a6f21a7df363ae9cc29c4f9ad.jpg


Создание переменной-ссылки


Откройте WBP_HUD и переключитесь в режим Graph, перейдя в Editor Mode и выбрав Graph.

ccaf58661ae7b5c4223d2769fce97d91.jpg


Перейдите на вкладку My Blueprint и создайте новую переменную GameManager.

Перейдите в панель Details и нажмите на раскрывающийся список рядом с Variable Type. Найдите BP_GameManager и выберите BP Game Manager\Object Reference.

b721409483d7d2fcc5e10097d0cd847c.jpg


Задание переменной-ссылки


Нажмите на Compile и откройте BP_GameManager.

Найдите нод Create Widget и перетащите левой клавишей мыши контакт Return Value. Отпустите левую клавишу на пустом пространстве и выберите из меню Set Game Manager.

Затем соедините нод Add to Viewport с нодом Set Game Manager.

a07eb0ec7ff3b66aeda853b6924ae689.jpg


Примечание: можно перенаправлять «провода», дважды щёлкая на них для создания нода Reroute. Перетащите левой клавишей мыши нод Reroute, чтобы перенаправить «провод».

Затем создайте нод Self и соедините его с левым контактом нода Set Game Manager. Нод Self будет указан в списке как Get a reference to self.

0776d8f5f55065d60a9104c05feea9a9.jpg


Теперь, когда WBP_HUD уже создан, у нас будет ссылка на BP_GameManager.

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

Функции


Функции в Blueprints — это графы, похожие на Event Graph. В отличие от Event Graph функции можно вызывать с помощью нодов. Зачем же это может понадобиться?

93a91d1a748c74dfb0277857b51087b9.jpg


Упорядоченность


Одна из причин для использования функций — упорядоченность. Благодаря функциям можно соединить несколько нодов в один.

Посмотрите на раздел Event BeginPlay в BP_GameManager. Здесь есть две функции: Restart и SetUpCamera.

44b31de14d9a82e6573d540013e63f45.jpg


Вот как будет выглядеть этот раздел без функций:

6d690ca6e60d4db42fd4871ede4cff39.jpg


Как вы видите, благодаря функциям он выглядит гораздо чище.

Повторное использование


Ещё одна причина для применения функций — повторное использование. Например, если вам нужно сбросить счётчик и таймер, то это можно запросто сделать с помощью функции Restart.

9ab6c99b43065f7c26acfffb3f327ef0.jpg


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

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

Обновление виджета


При создании виджета также автоматически создаётся и переменная-ссылка на этот виджет. Однако по умолчанию виджеты Text не имеют переменных-ссылок. Это значит, что мы не сможем задавать их свойство Text. К счастью, это легко исправить.

Нажмите на Compile и откройте WBP_HUD. Переключитесь в режим Designer.

Выберите CounterText, а затем перейдите в панель Details. Убедитесь, что в самом верху есть флажок Is Variable.

8ea52f1114e05e5edda2a2f257d6fd99.jpg


Теперь мы сможем обновлять CounterText. Следующим шагом будет создание функции для обновления текста.

Создание функции обновления


Переключитесь обратно в режим Graph и перейдите во вкладку My Blueprint. Нажмите на значок + справа от раздела Functions.

85f32cfc2846c2858559f342ee626718.jpg


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

По умолчанию граф будет содержать нод Entry. При выполнении функции она будет начинать с этого нода.

ff5133f66c1f50f6bedf26a4d4d5ca8f.jpg


Чтобы CounterText отображал переменную ShapesCollected, нам нужно соединить их.

Перетащите в граф переменную GameManager. Перетащите левой клавишей мыши его контакт и отпустите на пустом пространстве. В меню выберите Get Shapes Collected.

5196ea85205f4480c74a2f3a71fcda36.jpg


Чтобы задать текст, нужно будет использовать нод SetText (Text). Перетащите переменную CounterText в граф. Перетащите левой клавишей мыши её контакт и отпустите на пустом пространстве. В меню добавьте нод SetText (Text).

d02723e041ff8fff9c5136ca3b355b85.jpg


SetText (Text) может получать входные данные типа Text. Однако переменная ShapesCollected имеет тип Integer. К счастью, Unreal автоматически выполняет преобразование при подключении Integer ко входу Text.

Соедините переменную ShapesCollected с контактом In Text нода Set Text (Text). Unreal автоматически создаст нод ToText (int).

38f50a49c81b43e6d882f3af633df5f4.gif


Чтобы доделать функцию, соедините нод Entry с нодом Set Text (Text).

579a0d353fb7c96cc06682eb1e98e99c.jpg


Порядок событий:

  1. При вызове UpdateCounterText функция получает переменную ShapesCollected от BP_GameManager
  2. Нод ToText (int) преобразует значение ShapesCollected в тип Text
  3. SetText (Text) задаёт текст CounterText из значения ToText (int)


Теперь нам нужно научиться вызывать UpdateCounterText, когда игрок ловит фигуру.

Вызов функции обновления


Лучше всего вызывать UpdateCounterText сразу после того, как игра увеличивает значение ShapesCollected. Я создал функцию IncrementShapesCollected, которая выполняет инкремент счётчика. Фигуры вызывают эту функцию, когда сталкиваются с игроком.

5eee766ce8ea62d3ac2ef79f4e34cbd6.jpg


Нажмите на Compile и вернитесь в BP_GameManager.

Прежде чем вызвать UpdateCounterText, нам нужна ссылка на WBP_HUD. Попробуйте создать переменную-ссылку самостоятельно!

Решение внутри
  • Найдите раздел, в котором мы создавали и отображали WBP_HUD.
  • Перетащите контакт Return Value нода Create Widget.
  • Отпустите левую клавишу мыши на пустом пространстве и выберите в меню Promote to variable.
  • Добавьте новый нод в конец цепочки нодов


После создания переменной измените её имя на HUDWidget.

a4d2b516087ebca8f257d673c1687e48.jpg


Затем перетащите правый контакт нода Set HUDWidget и отпустите на пустом пространстве. Добавьте нод UpdateCounterText. Благодаря этому CounterText будет отображать значение ShapesCollected при запуске игры.

4263bfec0f157f63f4f372628751347e.jpg


Затем перейдите в панель My Blueprint и зайдите в раздел Functions. Дважды щёлкните на IncrementShapesCollected, чтобы открыть его граф.

32e9741313fe64071a2339a4120ee42e.jpg


Перетащите переменную HUDWidget в граф. Перетащите его контакт и отпустите на пустом пространстве. Добавьте нод UpdateCounterText и соедините его следующим образом:

92470e4dc228df6539a042dabf893752.jpg


Теперь при выполнении IncrementShapesCollected она будет увеличивать ShapesCollected, а затем вызывать UpdateCounterText. Эта функция затем обновит CounterText значением ShapesCollected.

Нажмите на Compile и закройте BP_GameManager. Нажмите на Play и соберите несколько фигур, чтобы увидеть, как обновляется виджет CounterText.

6563561c99534e84d758cc2d0d743ccf.gif


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

Bindings


Привязки позволяют автоматически обновлять определённые свойства виджетов. Чтобы иметь возможность привязки, свойство должно обладать раскрывающимся списком Bind.

639ff70717b77e4de42cd751528860ce.jpg


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

d595c6d4f499df9c4cb2ce27fff20526.jpg


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

С учётом этого привязки хорошо подходят для часто изменяющихся элементов, таких как таймеры. Давайте создадим привязку для TimerText.

Создание привязки


Откройте WBP_HUD и переключитесь в режим Designer.

Выберите TimerText, а затем перейдите в раздел Content панели Details. Вы увидите, что свойство Text имеет возможность привязки. Нажмите на раскрывающийся список Bind и выберите Create Binding.

02afeb3dbd2770a2a5a3f905c428326a.jpg


При этом создастся новая функция и выполнится переход к её графу. Переименуйте функцию в UpdateTimerText.

Функция будет иметь нод Return с контактом Return Value типа Text. TimerText будет отображать любой текст, который вы подключите к этому контакту.

9a51dbb7f88846d1d2c00c85232e4f59.jpg


Перетащите GameManager на граф и получите из него переменную TimeRemaining.

Соедините переменную TimeRemaining с Return Value нода Return. Как и в прошлый раз, Unreal автоматически добавит нод преобразования.

b9959c73757d46b22b181eb305ff855f.jpg


Подведём итог:

  • Привязка будет постоянно вызывать функцию UpdateTimerText
  • Функция получает переменную TimeRemaining от BP_GameManager
  • Нод ToText (float) преобразует значение из TimeRemaining в тип Text.
  • Преобразованное значение затем выводится в нод Return


Наш HUD наконец готов. Нажмите на Compile и закройте WBP_HUD. Нажмите на Play, чтобы увидеть окончательные результаты.

8df3bdac62860761f3ce78e694888c2a.gif


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


Готовый проект можно скачать здесь.

Теперь вы знаете основы UMG и достаточно просто можете создавать более сложные интерфейсы. Попробуйте поэкспериментировать с другими виджетами и виджетами панелей.

Если вы хотите узнать, что делают другие виджеты, то прочитайте страницу Widget Type Reference в документации Unreal Engine.

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

© Habrahabr.ru