12 декабря 2021

🎮 Как использовать Blueprint для быстрого прототипирования игр на Unreal Engine 4?

UE4 C++ Developer. Currently working with Flying Wild Hog on the Space Punks title. Author of articles on C++, GameDev, Unreal Engine and general programming
Изучение Blueprint – лучший вариант быстрого старта в Unreal Engine. Статья проведёт читателей по основам Blueprint, расскажет о его применении и даст несколько советов по поводу баланса между C++ и BP.
🎮 Как использовать Blueprint для быстрого прототипирования игр на Unreal Engine 4?

Что нужно знать перед прочтением этой статьи?

Предполагается, что читатель уже базово знаком с Unreal Engine 4: умеет создавать проекты, приблизительно знает, где в интерфейсе что находится, а также имеет представление об основных концепциях движка. Также предполагается, что читатель знаком с программированием на любом императивном языке и имеет опыт работы с отладчиком (вне зависимости от языка программирования и IDE).

Примечание
В статье используется UE 4.27.1.

Что такое Blueprint?

Под Blueprint (BP) могут подразумевать две связанные, но всё-таки разные вещи: систему визуального программирования и созданные с помощью этой системы объекты. Суть этой системы заключается в программировании игровой логики посредством основанного на графах скриптового языка.

Пример графа Blueprint
Пример графа Blueprint

Как и многие подобные языки, он позволяет создавать классы и их экземпляры (объекты), которые зачастую называют просто блюпринтами.

Больше полезной информации вы найдёте на нашем телеграм-канале «Библиотека программиста».

Для чего нужен Blueprint?

Как ни странно, Blueprint нужен не только для программирования акторов (игровых объектов) и подобных игровых вещей. Приведём несколько примеров использования этого инструмента:

  • UI: логика для виджетов может быть реализована в Blueprint;
  • Анимации: в Unreal Engine есть понятие Animation Blueprint – фактически это особая версия BP, в которой имеются дополнительные фичи и целью которой является описание анимационной части поведения объекта;
  • Расширения редактора: с помощью BP можно создавать так называемые Editor Utility, фактически представляющие собой скрипты для редактора. Их можно использовать для автоматизации рутинных задач или даже для серьезного дополнения возможностей редактора.

Какова польза от Blueprint?

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

Если же говорить о BP имея в виду классы и экземпляры классов, то их главным преимуществом является, пожалуй, возможность визуального создания игровых объектов: расположение различных компонентов (компонент – обособленная функциональность, предназначенная для переиспользования) в пространстве, а также их настройка прямо из редактора. Это аналогичная упомянутой в предыдущем абзаце идея, только касающаяся не логики, а данных – настроек компонентов.

Обзор редактора Blueprint и создание класса

Для создания BP-классов существует несколько путей, но основной – сделать т.н. ассет Blueprint из панели Content Browser. Необходимо щёлкнуть правой кнопкой мыши в Content Browser и выбрать Blueprint Class:

Создание BP-класса
Создание BP-класса

На следующем шаге необходимо выбрать базовый класс для создаваемого:

Выбор базового класса
Выбор базового класса

По двойному щелчку на созданном классе будет открыт редактор BP. Остановимся на нём (на скриншоте ниже создан BP-класс, наследник класса Actor):

Редактор BP
Редактор BP
  • В левой верхней части расположена панель, где отображается иерархия компонентов BP-класса. Сразу под ней – панель, отражающая структуру класса: функции, BP-графы, макросы, переменные и делегаты.
  • В средней части окна редактора расположена основная рабочая область, поделенная на вкладки. Вкладок может быть гораздо больше чем показано на скриншоте: к примеру, для каждой новой функции класса будет создана отдельная.
  • Вкладка “Viewport”, отображает все компоненты класса в пространстве так, как они будут расположены при создании экземпляра этого BP-класса в игре. Здесь разработчик может перемещать, вращать и масштабировать компоненты, немедленно наблюдая результат манипуляций.
  • Вкладка “Construction Script” позволяет задать функцию, вызываемую при создании экземпляра этого класса. Знакомые с объектно-ориентированным программированием читатели уже поняли, что речь идёт о BP-версии обычного конструктора. Вкладка “Event Graph” содержит логику обработки воспринимаемых классом событий.
  • Панель “Details” в левой части редактора, отображает настройки самого класса, одного из его компонентов, переменной класса и т.д.
  • Основная часть описанных ниже манипуляций будет сосредоточена во вкладке Event Graph и панели “My Blueprint”.

Переменные в Blueprint

Создадим унаследованный от Actor BP-класс, назовём его Bulb и с помощью кнопки “Add Component” добавим в него точечный источник света (Point Light Component):

Добавление компонента
Добавление компонента

Создадим простую сцену, на которой расположим выполняющий роль пола куб, а посередине – наш актор:

Тестовая сцена
Тестовая сцена

Запустим проект и убедимся, что куб освещён скучным белым светом:

Скучный белый свет
Скучный белый свет

Вернёмся к нашему BP и с помощью кнопки “+ Variable” создадим новую переменную, используя которую будет можно задать цвет источника света. Назовём её LightColor, выделим в секции “Variables” панели “My Blueprint” и переключимся на панель “Details”, где будут отображены настройки переменной:

Добавление переменной
Добавление переменной
Настройки переменной
Настройки переменной

Выберем тип “Linear Color” и установим флаг “Instance Editable”. Этот флаг позволит задавать значение переменной для каждого экземпляра класса.

Данные есть, теперь к логике. Перейдём в “Event Graph” к событию BeginPlay. Это событие запустится, когда актор будет добавлен (заспаунен) на уровень. Потянем левой кнопкой мыши за отмеченный сиреневым элемент:

Событие <code class="inline-code">BeginPlay</code>
Событие BeginPlay

Отпустим левую кнопку мыши – тут же появится окно выбора функции. Снимем флаг “Context Sensitive” (без него редактор предложит все возможные функции, а не только те, которые укладываются в текущий контекст) и найдём функцию Set Light Color компонента Point Light Component:

Панель выбора действия
Панель выбора действия

Должно получиться так:

Установка цвета на срабатывании <code class="inline-code">BeginPlay</code>
Установка цвета на срабатывании BeginPlay

Белая линия означает порядок выполнения. Иначе говоря, белая линия – это возможный путь передачи управления из одной BP-ноды (узла BP-графа) в другую.

Цветные пины (pin) означают входные данные/выходные данные. Как видно на скриншоте, Set Light Color ожидает три значения на входе:

  • Target – объект, на котором будет вызвана функция. Здесь ожидается Point Light Component. Перетянем его зажатой левой кнопкой мыши из секции “Variables” панели “My Blueprint”. После отпускания кнопки мыши, появится выбор:
Выбор между геттером и сеттером
Выбор между геттером и сеттером

Нас интересует получение компонента, а не его перезапись, поэтому выбираем Get PointLight. Соединим выходной пин создавшейся ноды с входным пином Target ноды-вызова Set Light Color:

Передача значения <code class="inline-code">PointLight</code> в функцию
Передача значения PointLight в функцию
  • New Light Color – новый цвет для источника света. Его необходимо взять из созданной нами переменной LightColor. Для закрепления проделайте это сами.
  • sRGB – булев флаг. Значение задано на самой ноде: флаг отмечен – значение истино, снят – ложно.

После изменения BP его необходимо скомпилировать. Для этого есть специальная кнопка на панели инструментов в верхней части редактора.

Тулбар BP-редактора. Кнопка для компиляции – крайняя слева
Тулбар BP-редактора. Кнопка для компиляции – крайняя слева

При успешной компиляции кнопка будет гореть зелёным:

Индикатор успешной компиляции
Индикатор успешной компиляции

Перейдем к редактору уровня, выделим наш актор и обратимся к панели “Details”:

Свойства актора
Свойства актора

В разделе “Default” можно видеть созданную нами переменную Light Color. Нажмем на квадратно-чёрную её часть и выберем какой-нибудь цвет. Запустим проект и убедимся, что источник света изменил цвет:

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

Операторы ветвления

Вернёмся к переменной LightColor и сделаем из неё массив. Для этого необходимо нажать на иконку справа от типа переменной и выбрать иконку с квадратами три на три:

Выбор модификации переменной: одиночное значение, массив, множество и карта/словарь (map)
Выбор модификации переменной: одиночное значение, массив, множество и карта/словарь (map)

Вероятно, редактор попросит вас подтвердить: действительно ли вы хотите изменить тип переменной: смело отвечайте “Да”. После этого ваш граф сломается:

Граф сломался
Граф сломался

Чтобы это исправить, удалите ноду с LightColor и перекомпилируйте BP.

Помимо массива и отдельной переменной имеется множество (Set) и карта (Map).

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

Создадим новую переменную CurrentColorIndex типа Integer, скомпилируем BP и зададим переменной значение по умолчанию – 0:

Значение переменной по умолчанию
Значение переменной по умолчанию

Создадим такую конструкцию:

Мне надо подписать картинку, но я сам не знаю, что здесь происходит
Мне надо подписать картинку, но я сам не знаю, что здесь происходит
Непонятно? Позвольте объяснить.
Главная нода, на которую стоит обратить внимание – Branch, представляющая собой обыкновенный if. На входе – булево значение, выходы – это передача управления либо по одному пути, либо по другому.

Булево значение является результатом строгого сравнения значения CurrentColorIndex и размера массива LightColor. Внимательный читатель догадывается к чему идёт дело: к наивной реализации цикла.

Правая часть блюпринта
Правая часть блюпринта

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

Итоговый блюпринт целиком
Итоговый блюпринт целиком

Добавим цветов в находящийся на уровне актор:

Массив цветов внутри нашего актора
Массив цветов внутри нашего актора

Запустив игру, проверим результат.

Циклы

Конструкцию выше можно с легкостью заменить на встроенный в BP цикл For Each:

Встроенная в BP нода-цикл
Встроенная в BP нода-цикл
На вход ноде цикла приходит контейнер для перебора, на выходе два пина для передачи управления – тело цикла и завершение цикла, а также два пина данных – текущий элемент и индекс текущего элемента.

Запустим и убедимся, что всё работает. Ничего не работает? У меня тоже.

Это старый баг движка (или фича – уже никто не знает). Delay внутри For each игнорируется, поэтому цикл выполняется настолько быстро, что становится невозможным заметить реальную смену цвета, а лишь только черный экран.

Примечание
Варианты обхода этого бага

Функции

Для возможности визуального отслеживания результатов работы нашего BP, вернём вариант с Branch. В секции “Functions” панели “My Blueprint” создадим новую функцию SetColor:

Создание новой функции
Создание новой функции

В панели “Details” добавим параметр для функции с именем NewColor и типом Linear Color:

Создание нового параметра функции
Создание нового параметра функции

Заметьте, для новой функции в рабочей области BP-редактора появилась новая вкладка. Перенесём туда вызов функции SetLightColor на компоненте Point Light:

Функция Set Color
Функция Set Color

Заменим оба вызова Set Light Color в Event Graph на вызов нашей функции:

Обновлённый блюпринт
Обновлённый блюпринт

Запустим проект и убедимся, что всё работает, как и работало.

Отладка Blueprint

В редакторе имеется встроенный отладчик, позволяющий устанавливать точки останова и просматривать значение переменных в BP прямо во время выполнения. Поставим точку останова на вызове Set Light Color в функции Set Color. Для этого кликните по ноде правой кнопкой мыши и выберите “Add Breakpoint”:

Брейкпоинт на BP-ноде
Брейкпоинт на BP-ноде

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

Сработавший брейкпоинт
Сработавший брейкпоинт

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

Тулбар дебаггера
Тулбар дебаггера

Баланс между C++ и Blueprint

Какую часть проекта стоит реализовать в Blueprint, а какую – написать на C++? Здесь нет однозначных ответов: они варьируются от проекта к проекту, однако есть несколько рекомендаций.

Для начала сравним C++ и Blueprint.

C++

  • Код на C++ работает быстрее;
  • C++ гораздо лучше справится с задачами, связанными с булевой логикой, математикой и большими системами;
  • На C++ рекомендуется рекомендуется писать ядро игры и реализовывать критически важную функциональность;
  • Часть функциональности и определённых в C++ данных можно держать закрытыми для Blueprint, не загрязняя ими API.

Blueprint

  • Достаточно производительный, но медленнее C++;
  • Blueprint гораздо проще использовать для прототипирования;
  • Гораздо более быстрые итерации разработки, чем при использовании C++;
  • Больше участников команды могут работать с Blueprint;
  • Фактически каждый BP является ассетом. Иначе говоря BP – это контент. В некоторых случаях это важно.
В двух словах:
C++ лучше использовать для core-функциональности, в то время как BP – для чего-либо специфичного для отдельно-взятого уровня или зависимого от другого контента. Ещё один вариант, когда BP предпочтительней – это нередкий случай, в котором необходимо создать содержащий несколько компонентов объект, особенно если расположение этих компонентов в пространстве важно. Объекты такого рода гораздо удобнее создавать/изменять в редакторе, с чем и связана рекомендация реализовывать их через BP.

Для команды из одного человека и небольшого проекта вполне приемлемо использовать только BP. Использование же только C++ (в любом проекте) вряд ли является обоснованным решением.

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

Вывод

Blueprint – система визуального программирования в UE, призванная увеличить количество участников команды, способных самостоятельно создавать игровую логику. Блюпринтами также называют определённые через эту систему классы. BP поддерживает все фичи обычных языков программирования: переменные, функции, операторы ветвления, циклы. В редакторе UE имеется встроенный отладчик BP. Перед началом проекта стоит серьезно задуматься о балансе между C++ и BP, которого стоит придерживаться.

***

Несмотря на кажущуюся простоту BP – система с большим количеством тонкостей, в особенности если смешивать её с C++. Полное изучение всех особенностей работы с BP может занять время. Удачи в обучении!

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ