Разработчики видеоигр используют графику и текст для отображения необходимой информации, например, здоровья или очков. Это называется интерфейсом пользователя (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: Системы частиц
- Часть 9: Искусственный интеллект
- Часть 10: Как создать простой FPS
Приступаем к работе
Скачайтезаготовку проектаи распакуйте её. Перейдите в папку проекта и откройтеGeometryCatcher.uproject.
Примечание:если откроется окно, сообщающее, что проект создан в более ранней версии Unreal editor, то всё в порядке (движок часто обновляется). Можно или выбрать опцию создания копии, или опцию преобразования самого проекта.
Нажмите наPlay, чтобы начать управлять белым кубом и попробуйте ловить падающие фигуры. Куб можно перемещать горизонтально с помощью мыши. Через десять секунд фигуры перестают создаваться.
Первое, что нужно сделать — создать HUD-дисплей, отображающий две вещи:
- Счётчик, отслеживающий количество собранных игроком фигур
- Таймер, отображающий количество секунд, оставшихся до завершения создания фигур
Чтобы создать всё это, нам необходимывиджеты.
О виджетах
Виджет (widget) — это элемент UI, предоставляющий UI визуальные функции. Например, виджет Button предоставляет объект, который пользователь может видеть и нажимать на него.
Сам виджет необязательно должен быть видимым. Например, виджет Grid Panel равномерно разделяет своё пространство между его содержимым. Пользователь не может увидеть Grid Panel, но видит его воздействие.
Кроме того, виджеты могут содержать другие виджеты. Вот пример виджета, содержащего виджет Text (метка Name) и виджет Text Box:
Можно даже создать виджет, являющийся целым интерфейсом, например, экраном меню. Ниже представлен пример виджета, созданного так, чтобы он выглядел как начальный экран игры. Все элементы UI тоже являются виджетами и содержатся внутри виджета начального экрана.
Итак, мы узнали, что такое виджеты. Теперь можно создать виджет для HUD.
Создание виджета
Перейдите в Content Browser и найдите папкуUI. Нажмите на кнопкуAdd Newи выберитеUser Interface\Widget Blueprint. Переименуйте новый ассет вWBP_HUD.
Дважды нажмитенаWBP_HUD, чтобы открыть его в UMG UI Designer.
UMG UI Designer
UMG UI Designer состоит из семи основных элементов:
- Designer:в этой области представлено визуальное отображение виджета. Перемещаться по ней можнозажав правую клавишу мышиидвигая мышью. Масштабирование выполняетсяпрокруткойколёсика мыши.
- Details:здесь отображаются свойства выбранного виджета
- Palette:список всех виджетов, которые можно использовать. Все созданные пользователем виджеты тоже появляются здесь.
- Hierarchy:список всех уже используемых виджетов
- Animations:некоторые свойства виджетов могут иметь анимацию, например, расположение и размер. В этой панели перечислены все анимации.
- Timeline:при выборе анимации на этой панели показываются анимированные свойства и ключевые кадры
- Editor Mode:здесь можно переключаться между режимами Designer и Graph. Режим Graph почти аналогичен Event Graph у Blueprint.
Создание виджета Text
Виджеты Text отлично подходят для отображения числовой информации, например, счётчика и таймера.
Перейдите в панель Palette и найдите виджетText. Добавьте виджет,перетащив его мышьюв панель Designer.
Содержание текста нас пока не интересует, мы изменим его позже.
Переименуйте виджет вCounterText. Это можно сделать, выбрав виджетTextи перейдя в панель Details. ВведитеCounterTextвтекстовое полев верхней части.
Виджеты можно двигать,перетаскивая их левой клавишей мышив панели Designer.
Также можно изменять размер виджетов,перетаскивая левой клавишей мышиграницы. Изменение размера позволяет задать границы виджета. Unreal не будет ничего рендерить за пределами границ.
Или же можно задать положение и размер изменением значений в панели Details. Задайте следующие свойства и значения дляCounterText:
- Position X:200
- Position Y:50
- Size X:500
- Size Y:100
На данный момент текст занимает только небольшую часть поля.
Можно увеличить размер шрифта, перейдя в панель Details и к разделуAppearance. Справа от свойстваFontесть текстовое поле для задания размера шрифта.
Введите размер68.
Давайте сделаем счётчик красивее, добавив к нему значок.
Создание виджета Image
Виджеты Image — это простой способ отображения графики в UI, например, значков.
Создайте виджетImageи назовите егоCounterIcon. ЗадайтеPosition Xзначение75, аPosition Y— значение50. Виджет разместится рядом сCounterText.
Чтобы задать изображение, перейдите в панель Details и зайдите в разделAppearance. Разверните свойствоBrush, а затем нажмите нараскрывающемся спискерядом сImage. ВыберитеT_Counter.
Изображение будет выглядеть растянутым, потому что размер виджета отличается от размера изображения.
Вместо изменения размера виджета мы можем использовать опциюSize To Content. Эта опция автоматически изменяет размер виджета под размер его содержимого.
Находясь в панели Details, перейдите в разделSlot (Canvas Panel Slot). Поставьтефлажокрядом сSize To Content.
Виджет сам подгонит свой размер под изображение.
Если игра будет запускаться с разными размерами экрана, то UI должен соответствующим образом перемещать виджеты. Чтобы сохранить компоновку UI, нужно использоватьпривязки.
Привязки
Точка привязки задаёт место, относительно которого определяется положение виджета. По умолчанию виджеты привязаны к верхнему левому углу своего родительского элемента. Поэтому когда мы задаём положение виджета, мы на самом деле указывает положение относительно этой точки привязки.
В примере ниже каждое изображение привязано к одной точке (к ближайшему углу).
Заметьте, что каждое изображение сохраняет положение относительно своей привязки. Благодаря привязкам UI будет иметь одинаковое расположение при разных размерах экрана.
Также можно использовать привязки для автоматического изменения размера виджетов. В случае привязки к двум или более точкам виджет будет менять свой размер для сохранения относительного размера.
В примере ниже индикатор привязан к верхнему левому и верхнему правому углам.
Вертикально индикатор перемещается, но не меняет размера, потому что на оси Y есть только одна привязка (сверху). Однако горизонтально индикатор меняет размер, потому что имеет две точки привязки по оси X.
Anchor Medallionотображает расположение привязки. Он появляется при выборе виджета.
Привязки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 (это выровняет текст по правой стороне виджета)
Теперь мы хотим задать привязку в правом верхнем углу. Это можно сделать,перетащив мышьюкругвAnchor Medallion. ПереместитеAnchor Medallionвправый верхний угол.
Заметьте, как обновляется положение относительно привязки.
Создайте виджетImageи назовите егоTimerIcon. Задайте следующие свойства:
- Position X:1750
- Position Y:50
- Size To Content:Checked
- Brush\Image:T_Timer
Вместо задания привязки с помощью Anchor Medallion, можно воспользоваться предустановленными значениями. Перейдите в панель Details и нажмите нараскрывающийся списокрядом сAnchors, чтобы открыть предустановленные значения. Выберитетретьюсхему (с квадратом в правом верхнем углу).
Создание схемы UI на этом завершено. Можно убедиться в том, что привязки работают, эмулируя различные размеры экрана. Перейдите в панель Designer и нажмите на раскрывающийся списокScreen Size.
Выбор опции изменит размерWBP_HUDдля соответствия опции. Ниже показано, как HUD будет выглядеть на iPad Air. Заметьте, что виджеты стали ближе друг к другу.
В следующем разделе мы узнаем, как отображать виджетWBP_HUD.
Отображение HUD
Нажмите наCompile, а затем вернитесь в основной редактор. Перейдите в папкуBlueprintsидважды щёлкнитенаBP_GameManager, чтобы открыть его.
HUD должен становиться видимым после запуска игры. Для этого можно использовать нодEvent BeginPlay.
Найдите нодEvent BeginPlayи добавьте нодCreate Widgetв конец цепочки нодов. Этот нод создаёт экземпляр указанного виджета.
Нажмите нараскрывающийся списокрядом сClassи выберитеWBP_HUD.
Чтобы отобразить HUD, необходимо использовать нодAdd to Viewport.Перетащите левой клавишей мышиконтактReturn ValueнодаCreate Widget. Отпустителевую клавишу мышив пустом пространстве, чтобы открыть контекстное меню. Добавьте нодAdd to Viewport.
Давайте разберёмся с порядком событий:
- Когда Unreal спаунитBP_GameManager, выполняются функцииRestartиSetUpCamera. Эти функции настраивают несколько переменных и камеру. Если вы не знаете, что такое функция, то не волнуйтесь, скоро мы их рассмотрим.
- НодCreate Widgetсоздаёт экземплярWBP_HUD
- НодAdd to ViewportотображаетWBP_HUD
Нажмите наCompileи вернитесь в основной редактор. Нажмите наPlay, чтобы запустить игру с новым HUD.
Чтобы отобразить значения счётчика и таймера, нам нужны переменные для хранения этой информации. Эти переменные можно найти вBP_GameManager.
Чтобы использовать эти переменные. нам нужен способ получения доступа кBP_GameManagerизWBP_HUD. Для этого можно применитьпеременную-ссылку.
Переменные-ссылки
Хранить ссылки полезно, потому что они позволяют удобно получать доступ к экземплярам.
Представьте, что у вас есть одна коробка, в которой лежит мяч. Если нам нужно найти и изучить мяч, то это будет просто, потому что у нас есть всего одна коробка.
А теперь представьте, что у нас сотня коробок, но мяч есть только в одной. Нам нужно будет проверять каждую коробку, пока мы не найдём коробку с мячом.
Каждый раз, когда нам нужно будет изучить мяч, придётся выполнять эту операцию. Это быстро приведёт к проблемам с производительностью.
Благодаря ссылкам можно отслеживать коробку с мячом. Таким образом, нам не придётся проверять каждую коробку.
Создание переменной-ссылки
ОткройтеWBP_HUDи переключитесь в режим Graph, перейдя в Editor Mode и выбравGraph.
Перейдите на вкладку My Blueprint и создайте новую переменнуюGameManager.
Перейдите в панель Details и нажмите нараскрывающийся списокрядом сVariable Type. НайдитеBP_GameManagerи выберитеBP Game Manager\Object Reference.
Задание переменной-ссылки
Нажмите наCompileи откройтеBP_GameManager.
Найдите нодCreate Widgetиперетащите левой клавишей мышиконтактReturn Value. Отпустителевую клавишуна пустом пространстве и выберите из менюSet Game Manager.
Затем соедините нодAdd to Viewportс нодомSet Game Manager.
Примечание:можно перенаправлять «провода»,дважды щёлкаяна них для создания нодаReroute.Перетащите левой клавишей мышинодReroute, чтобы перенаправить «провод».
Затем создайте нодSelfи соедините его слевымконтактом нодаSet Game Manager. НодSelfбудет указан в списке какGet a reference to self.
Теперь, когдаWBP_HUDуже создан, у нас будет ссылка наBP_GameManager.
В следующем разделе мы узнаем, как обновлять виджет с помощьюфункций.
Функции
Функции в Blueprints — это графы, похожие на Event Graph. В отличие от Event Graph функции можно вызывать с помощью нодов. Зачем же это может понадобиться?
Упорядоченность
Одна из причин для использования функций — упорядоченность. Благодаря функциям можно соединить несколько нодов в один.
Посмотрите на разделEvent BeginPlayвBP_GameManager. Здесь есть две функции:RestartиSetUpCamera.
Вот как будет выглядеть этот раздел без функций:
Как вы видите, благодаря функциям он выглядит гораздо чище.
Повторное использование
Ещё одна причина для применения функций —повторное использование. Например, если вам нужно сбросить счётчик и таймер, то это можно запросто сделать с помощью функцииRestart.
Это позволяет сэкономить время на воссоздание нодов каждый раз, когда вам нужно сбросить эти переменные.
Мы познакомились с функциями и теперь можем использовать их для обновления виджетаCounterText.
Обновление виджета
При создании виджета также автоматически создаётся и переменная-ссылка на этот виджет. Однако по умолчанию виджеты Text не имеют переменных-ссылок. Это значит, что мы не сможем задавать их свойство Text. К счастью, это легко исправить.
Нажмите наCompileи откройтеWBP_HUD. Переключитесь в режимDesigner.
ВыберитеCounterText, а затем перейдите в панель Details. Убедитесь, что в самом верху есть флажокIs Variable.
Теперь мы сможем обновлятьCounterText. Следующим шагом будет создание функции для обновления текста.
Создание функции обновления
Переключитесь обратно в режимGraphи перейдите во вкладку My Blueprint. Нажмите на значок+справа от разделаFunctions.
При этом будет создана новая функция и вы перейдёте к её графу. Переименуйте функцию вUpdateCounterText.
По умолчанию граф будет содержать нодEntry. При выполнении функции она будет начинать с этого нода.
ЧтобыCounterTextотображал переменнуюShapesCollected, нам нужно соединить их.
Перетащите в граф переменнуюGameManager.Перетащите левой клавишей мышиего контакт и отпустите на пустом пространстве. В меню выберитеGet Shapes Collected.
Чтобы задать текст, нужно будет использовать нодSetText (Text). Перетащите переменнуюCounterTextв граф.Перетащите левой клавишей мышиеё контакт и отпустите на пустом пространстве. В меню добавьте нодSetText (Text).
SetText (Text)может получать входные данные типаText. Однако переменнаяShapesCollectedимеет типInteger. К счастью, Unreal автоматически выполняет преобразование при подключенииIntegerко входуText.
Соедините переменнуюShapesCollectedс контактомIn TextнодаSet Text (Text). Unreal автоматически создаст нодToText (int).
Чтобы доделать функцию, соедините нодEntryс нодомSet Text (Text).
Порядок событий:
- При вызовеUpdateCounterTextфункция получает переменнуюShapesCollectedотBP_GameManager
- НодToText (int)преобразует значениеShapesCollectedв типText
- SetText (Text)задаёт текстCounterTextиз значенияToText (int)
Теперь нам нужно научиться вызыватьUpdateCounterText, когда игрок ловит фигуру.
Вызов функции обновления
Лучше всего вызыватьUpdateCounterTextсразу после того, как игра увеличивает значениеShapesCollected. Я создал функциюIncrementShapesCollected, которая выполняет инкремент счётчика. Фигуры вызывают эту функцию, когда сталкиваются с игроком.
Нажмите наCompileи вернитесь вBP_GameManager.
Прежде чем вызватьUpdateCounterText, нам нужна ссылка наWBP_HUD. Попробуйте создать переменную-ссылку самостоятельно!
Решение внутри
После создания переменной измените её имя наHUDWidget.
Затемперетащите правый контактнодаSet HUDWidgetиотпуститена пустом пространстве. Добавьте нодUpdateCounterText. Благодаря этомуCounterTextбудет отображать значениеShapesCollectedпри запуске игры.
Затем перейдите в панель My Blueprint и зайдите в разделFunctions.Дважды щёлкнитенаIncrementShapesCollected, чтобы открыть его граф.
Перетащите переменнуюHUDWidgetв граф.Перетащитеего контакт иотпуститена пустом пространстве. Добавьте нодUpdateCounterTextи соедините его следующим образом:
Теперь при выполненииIncrementShapesCollectedона будет увеличиватьShapesCollected, а затем вызыватьUpdateCounterText. Эта функция затем обновитCounterTextзначениемShapesCollected.
Нажмите наCompileи закройтеBP_GameManager. Нажмите наPlayи соберите несколько фигур, чтобы увидеть, как обновляется виджетCounterText.
Теперь нам нужно научиться обновлять виджетTimerText, только другим способом, который называетсяпривязкой.
Bindings
Привязки позволяют автоматически обновлять определённые свойства виджетов. Чтобы иметь возможность привязки, свойство должно обладатьраскрывающимся списком Bind.
Можно привязывать свойства к функции или к переменной, хранящейся внутри виджета. Привязка постоянно получает значение от функции или переменной и присваивает привязанному свойству это значение.
Вы, наверно, задаётесь вопросом, почему же нельзя постоянно использовать привязки. Привязки неэффективны, потому что постоянно обновляются. Это значит, что игра тратит время на обновление свойства, даже если нет никакой новой информации. Сравните это с предыдущим способом, при котором виджет обновлялся только при необходимости.
С учётом этого привязки хорошо подходят для часто изменяющихся элементов, таких как таймеры. Давайте создадим привязку дляTimerText.
Создание привязки
ОткройтеWBP_HUDи переключитесь в режимDesigner.
ВыберитеTimerText, а затем перейдите в разделContentпанели Details. Вы увидите, что свойствоTextимеет возможность привязки. Нажмите нараскрывающийся список Bindи выберитеCreate Binding.
При этом создастся новая функция и выполнится переход к её графу. Переименуйте функцию вUpdateTimerText.
Функция будет иметь нодReturnс контактомReturn ValueтипаText.TimerTextбудет отображать любой текст, который вы подключите к этому контакту.
ПеретащитеGameManagerна граф и получите из него переменнуюTimeRemaining.
Соедините переменнуюTimeRemainingсReturn ValueнодаReturn. Как и в прошлый раз, Unreal автоматически добавит нод преобразования.
Подведём итог:
- Привязка будет постоянно вызывать функциюUpdateTimerText
- Функция получает переменнуюTimeRemainingотBP_GameManager
- НодToText (float)преобразует значение изTimeRemainingв типText.
- Преобразованное значение затем выводится в нодReturn
Наш HUD наконец готов. Нажмите наCompileи закройтеWBP_HUD. Нажмите наPlay, чтобы увидеть окончательные результаты.
Куда двигаться дальше?
Готовый проект можно скачатьздесь.
Теперь вы знаете основы UMG и достаточно просто можете создавать более сложные интерфейсы. Попробуйте поэкспериментировать с другими виджетами и виджетами панелей.
Если вы хотите узнать, что делают другие виджеты, то прочитайте страницуWidget Type Referenceв документации Unreal Engine.
Чтобы продолжить изучение, прочитайте следующий пост туториала, в котором я покажу вам, как соединить всё вместе для создания простой игры!