UnrealEd
Изучение Unreal Engine, документация на русском языке
Введение в Blueprints
Система Blueprints Visual Scripting в Unreal Engine – это полноценная система написания скриптов геймплея, в основу которой положена концепция использования интерфейса узлового типа, позволяющая создавать элементы геймплея прямо в Unreal-редакторе. Как и многие распространённые языки скриптования, он используется для определения объектно-ориентированные (OO) классы или объекты в движке. Если вы используете UE4, вы увидите, что объекты, использующие Blueprint, в народе называются просто «Blueprints».
Как работает Blueprints?
В своей базовой форме Blueprints виртуально скриптует дополнения к вашей игре. Сложные элементы геймплея создаются посредством связывания узлов, событий, функции и вариации в единую сеть.
Blueprints использует узловые сетки для реализации различных целей – конструирования объектов, единичных функций и общих событий геймплея, которые специфичны для каждого случая, создаваемого Blueprint — и внедрения нужного поведения и функциональности.
Часто использующиеся типы Blueprint
Самые распространенные типы Blueprint, с которыми вам придется работать – это Level Blueprints (уровни) и Blueprint Classes (классы).
Это всего два из всех доступных типов Blueprints, среди которых также есть Blueprint Macros (макросы) и Blueprint Interfaces (интерфейсы).
Level Blueprint
Level Blueprint играет ту же роль, что Kismet играл в Unreal Engine 3, и имеет те же возможности. Каждый уровень имеет свой собственный уровневый Blueprint, и он может управлять Актерами (Actors), контролировать все, что относится к фильмовой составляющей, используя Matinee Actors, и управлять вещами типа, стримов уровня, чекпоинтов и систем, связанных с уровнем. Level Blueprint также может взаимодействовать с Blueprint Classes, которые находятся внутри уровня, к примеру, читать/настраивать вариации или запускать кастомные события, которые те содержат.
Blueprint Class
Blueprint Classes идеальны для создания интерактивных ассетов, таких, как двери, выключатели, собираемые предметы и объекты разрушаемой среды. На картинке выше кнопка и набор дверей являются отдельными Blueprint, которые содержат необходимые скрипты, отвечающие, если игрок провоцирует определенные события, анимируя их, включая необходимые звуковые эффекты и изменяя их внешний вид (например, когда нажимают на кнопку, она начинает светиться).
В этом случае нажатие кнопки активирует событие внутри Blueprint, отвечающего за дверь, в результате чего она открывается. Но двери также можно с легкостью активировать с помощью другого типа Blueprint или с помощью Level Blueprint-последовательности. Благодаря самозаполняющейся природе Blueprints, их можно создать, просто набросав в уровни, и они будут прекрасно работать с минимумом настроек. Также это значит, что редактирование Blueprint, который используется в проекте, приведет к полному обновлению всех его структур.
Что еще умеет делать Blueprints?
Ниже приведены некоторые примеры того, как и для чего вы можете применить систему Blueprint.
Создание настраиваемых префабов (Prefabs) с помощью скриптов конструирования
В картах Content Examples длинные комнаты, которые содержат каждый пример (картинка сверху) – это отдельно взятый Blueprint, созданный из множества компонентов. Скрипт конструирования Blueprint создает и обслуживает различные Static Meshes (полигональные сетки) и подсветки, согласно параметрам, описанным в Blueprint’s Details panel (панель деталей). С помощью каждой созданной нами картой Content Example мы можете войти в демо-комнату Blueprint, установить значения, отвечающие за ее длину и высоту и количество комнат, которые можно сгенерировать (плюс некоторые другие опции), и в момент получить готовый набор комнат.
Первоначальное создание Blueprint, наподобие этого, может занять довольно много времени, но если вы знаете, что будете часто его использовать, то время, которое вы сэкономите на строительстве уровня и простоте внесения изменений, может нивелировать эту потерю.
Создание играбельного игрового персонажа
Pawns (пешки)- это еще один тип Blueprint Class, с помощью которого вы можете смиксовать вместе все необходимые элементы для создания играбельного персонажа в Blueprint-графе. Вы можете управлять поведение камеры, создавать вводимые события для мышки, контроллера и тач скринов, а также создавать Animation Blueprint- ассеты для управления скелетной сеткой анимации.
Когда вы начнете создавать нового Blueprint-персонажа, то столкнетесь с необходимостью добавления большого количества поведенческих компонентов, нужных для того, чтоб он мог передвигаться по территории, прыгать, плавать, падать, Для этого пригодятся вводимые события, координирующие то, как должен управляться персонаж.
Создание HUD
Скрипт Blueprint также может быть использован для создания игрового HUD, который похож на Blueprint Classes в том плане, что он может содержать событийные последовательности и вариации, но они не добавляются прямо к уровню, а применяются к ассету GameMode вашего проекта.
Вы можете установить HUD, чтоб читать вариации других Blueprints и использовать их для отражения шкалы здоровья, обновлять значение счета, отображать объектные маркеры и т.д. Также возможно использовать HUD для добавления зон поражения к таким элементам, как кнопки, которые можно кликнуть или в случае мобильных игр, получить реакцию на прикосновение к экрану.
Все примеры, описанные выше, существуют в контенте-образце, доступном в UE4, так что если вы хотите получше познакомиться с этими примерами, то их можно найти в Content Examples, в проектах First Person Shooter и Swing Ninja.
Blueprint Редакторы и графы
Разные типы Blueprint-редактора доступны в зависимости от типа Blueprint, над которым вы работаете. Корневая функция большинства Blueprint Editors – это Graph mode (режим графов), с центральным табом Graph, позволяющим разложить сеть вашего Blueprint.
Blueprint Interfaces. Unreal Engine 4 по-простому
Итак. Сегодня нам предстоит понять:
-что такое интерфейсы,
-почему интерфейсы иногда лучше и проще кастов,
-как взаимодействовать с объектами посредством интерфейсов,
-̶T̶h̶e̶ ̶L̶a̶s̶t̶ ̶O̶f̶ ̶U̶s̶ 2 ̶и̶л̶и̶ ̶C̶y̶b̶e̶r̶p̶u̶n̶k̶ ̶2̶0̶7̶7̶
Если нужно вызывать разные события в разных Blueprints.
Если нужно передавать информацию в разные Blueprints.
Ты можешь спросить: «Ну зачем мне эти интерфейсы? Я ведь функции и так могу через Cast To вызывать.»
Ну, я предполагаю, что если ты будешь в разных объектах вызывать разные функции, то у тебя получится доширак:
В таких случаях прибегают к использованию интерфейсов.
Создай функцию и назови её Interaction.
Я создал три разных блупринта.
Я хочу, чтобы когда я взаимодействовал с ними, они меняли цвет, но чтобы цвет всегда был разным(функцию смены цвета мы рассмотрели в предыдущей статье).
Теперь для каждого блупринта пропишем разную логику. Соответственно сфера пусть станет зелёной, куб красным, а конус синим.
После того, как прописали логику, в блупринте персонажа вызываем наш Message «Interaction»:
Не забудь добавить проверку на присутствие интерфейса в объекте!
Должно получиться так:
Как видишь, мы вызываем разные функции в блупринтах при помощи всего одного вызова интерфейса!
Если есть какие-то вопросы или есть некоторое непонимание каких-то моментов в статье, напиши об этом в комментариях. Я напишу понятнее.
Напоминаю, что вы также можете предлагать темы для следующих статей в комментариях. Я рассматриваю все варианты и не только по Unreal Engine.
Спасибо, что прочитали!
Валидацию всё таки лучше делать даже с интерфейсами. Всё что есть на сцене это Actor, без проверки на наличие интерфейса вы будете пытаться его вызвать при попадании куда угодно.
Это заметно скажется на производительности?
Если таких моментов будет много, то скажется, смотря сколько таких вызовов на кадр, но по большей части это правило хорошего тона. Если привыкнуть делать валидацию, то потом меньше забот с поиском багов, не вылетают неожиданные null exсeption, меньше крашей.
Я просто думал, что прелесть интерфейсов именно в том, можно запросы в любые объекты отправлять и в крайнем случае ничего не произойдёт, а проверка ведь тоже какое-то вычисление занимает. Неужели оно гораздо меньше, чем запрос без проверки?
Cast тоже довольно быстрый, это обычное приведение типов. Интерфейсы нужны для улучшения архитектуры, с ними проще работать, больше простора для реализации функционала. Выигрыш в скорости там спорный, скорее всего его нет вообще. Не нужно постоянно гнаться за оптимизацией кода, в первую очередь стоит добиваться правильной архитектуры. Если код по вашему мнению максимально оптимизирован, но в нём ничего не понятно и через месяц там не разберётся даже тот кто его писал, то это не очень хороший подход)
Создаёт лапшу программист, а не касты. Если вы видели кучу примеров лапши с кастами, это не значит что каст = плохо.
Проверки нужны для избежания ошибок, глобально ни быстрее, ни медленнее не станет, это не важно. Не надо везде пихать интерфейсы, ровно как и везде использовать каст. Почитайте про полиморфизм, всё что там есть применимо в какой-то степени и к БП.
Дабы не быть неправильно понятым, я поясню, что под лапшой подразумевал макаронных Монстров из блупринтов. ред.
Ну это понятно, макаронные монстры это как раз пример плохой архитектуры. В большинстве случаев они не так уж и медленно работают, просто добавить туда что-то или переделать довольно трудно.
Отличные такие министатьи! Спасибо!
Как раз изучаю потихоньку движок. Для понимания пригождается.
Что дальше в планах?)
Пока не знаю. Вот буду думать.
Диспатчеры. Массивы. Сеты, мэпы, перечисления, структуры. Циклы.
Жду статьи об sequencer. Ибо до сих пор не понимаю в чём его надобность
Отличная статья, помогла разобраться. Спасибо!
Blueprints и C++ в Unreal Engine: плюсы и минусы
Epic Games последовательно развивает систему визуального программирования Blueprints в Unreal Engine. Она продвигается как полноценная рабочая среда, в которой любой новичок может освоиться и собрать свою игру. Но действительно ли «блюпринты» ни в чём не уступают классическому программированию?
Александр Балакшин, программист AAA-игр, внёсший значительный вклад в разработку сезонных обновлений для Tom Clancy’s Rainbow Six Siege в роли старшего инженера-разработчика и лида геймплейной команды, разбирает плюсы и минусы Blueprints и объясняет её отличия от «чистого» C++.
Блюпринты выигрывают у C++ на начальных этапах разработки, особенно если код игры пишется с нуля. Они не требуют установки дополнительной среды, к тому же предлагают быстрые итерации. А блочный синтаксис блюпринтов понятен не только программистам, но и тем, кто знаком с аналогичными системами в программах для создания контента — например, художникам.
Но если рассматривать разработку игры в целом, в долгосрочной перспективе, то классический подход к программированию показывает свои преимущества. Даже сами Epic Games заостряют внимание на том, что блюпринты — это не код, а данные, поэтому и относиться к ним нужно соответственно. Например, некоторая общая логика всё равно должна выноситься в код.
По этой же причине блюпринты невозможно толком «мёрджить», то есть соединять результаты разработки. Поэтому их приходится отдельно блокировать, чтобы не создавать конфликтов и не терять проделанную работу. С классическим же кодом могут работать даже несколько человек одновременно, но результат их работы в одном файле обычно всё равно очень просто совместить.
Наконец, блюпринты бьют по производительности, так как компилируются в байт-код, который работает на встроенной в движок виртуальной машине. Да, их можно нативизировать, — то есть преобразовать Blueprint-логику в файлы C++, но даже разработчики из Epic рекомендуют этим не злоупотреблять.
Да и с точки зрения GOMS-анализа нажатие на клавишу клавиатуры оказывается быстрее, чем перемещение мышки. Это ни в коем случае не отменяет удобство визуального редактора, но, по моему опыту, с автодополнениями и прочими синтаксическими функциями современных IDE писать код удобнее и быстрее, чем создавать граф в блюпринтах. Хотя полезные сочетания клавиш и шорткаты в Unreal Engine тоже облегчают жизнь.
Я считаю, что если программисту нужно работать с Tick-функциями, или он использует какую-то сложную математику и пространственные запросы (например, LineTrace), всё это лучше вынести в С++. Отчасти из-за всех перечисленных особенностей Epic Games раздумывают над созданием отдельного скриптового языка для реализации игровой логики в Unreal Engine.
Тем не менее, блюпринты — достаточно мощный инструмент, который в Unreal Engine 4 используется не только для построения игровой логики, но и для работы с анимацией и системой эффектов Niagara. Поэтому каждая студия должна сама найти подходящий баланс между Blueprints и С++. Например, технические дизайнеры Riot Games использовали блюпринты в Valorant только для создания способностей игроков.
Сами Epic Games рекомендуют использовать блюпринты, когда в проекте очень много ссылок на контент, а его логика работает в первую очередь на визуальную составляющую. Также они пригодятся в создании прототипов, прямолинейной или редко используемой логики, которая не является частью основной архитектуры. Всё, что не получит преимуществ в С++ с точки зрения производительности, масштабируемости и стабильности, тоже может быть создано в Blueprints.
Ну а с С++ лучше работать, если функционал используется более чем в одном месте и включает в себя сложные элементы — например, сохранение игр или сетевой код. Если проект в дальнейшем будет расширяться, то его тоже лучше создавать с помощью классического программирования — оно помогает тщательно поддерживать логику и стабильность кода.
Словом, с любыми важными переменными, перечислениями и типами данных C++ работает лучше. Но и работа в Blueprints не отменяет классический подход, а только органично дополняет его в необходимых случаях. Так что разработчикам от визуального программирования никуда не деться.
Взаимодействие между Blue Print (Interface, Cast To)
Статья предназначена для новичков в Unreal Engine 4, и в ней разбираются способы взаимодействия между Blue Print (BP) в сцене.
Немного теории
BP в Unreal Engine 4 — это класс в понятии программирования, то есть, абстрактное описание алгоритмов и переменных, заключенных в контейнере. Пока BP не помещен в сцену (то есть, не создан объект), нельзя с ним проводить какие-либо операции, кроме создания объекта (instance) на его основе.
Например, у вас есть проигрывающий музыку BP, с названием BP_playMusic.Чтобы он заработал и начал проигрывать музыку, надо или поместить этот BP на сцену (создать объект/instance класса BP_playMusic) или создать instance из другого BP, находящегося на сцене. Этот объект будет иметь свое личное состояние переменных, и если вы поместите на сцену несколько таких объектов, это будут независимые друг от друга actors-объекты, хотя они созданы из одного BP, и у них общий класс BP_playMusic. Если у одного из этих объектов сменить музыкальный трек, то на другие объекты класса BP_playMusic это никак не повлияет.
Интерфейсы
Основной инструмент для взаимодействия BP — это интерфейс (interface). Интерфейс это объявление о том, что данный BP имеет обработчик функций, описанных в интерфейсе.
Например, есть BP лампы и сигнализации bp_lamp и bp_alarm, и мы хотим, чтобы в этих BP были функции turnOn и turnOff. Создаем интерфейс, назовем, например, I_turnAbleItem.
В интерфейсе добавим функции turnOn и turnOff. Также мы хотим, чтобы можно было регулировать величину яркости или громкости соответствующих actors-объектов. Добавим функцию setValue, и в ней параметр (float) value. Функции в интерфейсе выглядит неактивными, и в рабочей области ничего нельзя делать. Это потому, что тут задается только само название функций и их параметры, а реализация логики будет уже в самом BP.
Теперь можно скомпилировать, сохранить и закрыть интерфейс.
Открываем BP bp_lamp и bp_alarm, переходим в настройку и добавляем интерфейс I_turnAbleItem и компилируем BP.
Тем самым мы указали, что в этих BP есть обработчики событий turnOn, turnOff, setValue, и можем задать функционал для этих событий.
Теперь если в Event Graph вызвать контекстное меню и вписать turnOn, высветятся три строки в разных категориях:
Add Event — добавить обработчик события. Именно это используется для выполнение логики при активации из другого BP:
Здесь нам нужен Add Event, добавляем все 3 ивента, описанных в интерфейсе, и описываем логику, которая должна выполняться при их вызове.
Наши BP готовы. В качестве активатора создадим простой BP_Button с триггером. Если персонаж вошел в область триггера — включаем лампы и сигнализации, ставим им value = 10. Когда выходит — выключаем. Если в сцене уже установлены конкретные лампы и алерты, то можно их задать в actors-объекте BP_Button как переменные или массив объектов, но что делать, если таких объектов несколько сотен, или они генерируются динамически, и до запуска игры их нет на сцене? Для этого можно применить функцию GetActorWithInterface. Она вернет массив всех объектов на сцене, у которых есть заданный интерфейс. Используем эту функцию, в качестве интерфейса выберем I_turnAbleItem. В цикле по полученному массиву объектов у каждого из них вызовем ивент turnOn.
Нода turnOn будет показана со знаком конверта (message), обозначающим, что это вызов функции интерфейса. Так же добавляем вызов setValue. В нем будет возможность поставить значение value, которое мы указали в интерфейсе. По аналогии, на событие endOverlap ставим turnOff. Результат будет выглядеть примерно так:
Теперь сколько бы мы не поставили ламп и алертов на сцене, в них всех будут срабатывать эти события при взаимодействии персонажа с триггером.
Преимущество вызовов функций интерфейс через message еще в том, что их можно попытаться вызвать у любого BP, даже если у него нет интерфейса с вызываемой функцией, в таком случае просто ничего не произойдет. Например, мы получили ссылку на некий actor-объект в результате Trace и не знаем, что это за actor-объект, но мы хотим, чтобы он мог показать какой-то текст при наличии функции показа текста. Для таких случаев можно использовать интерфейс вызов (message). Если у actor-объекта будет вызываемый обработчик, он сработает.
Усложним задачу. Нам надо убедится, что лампа включилась, а если нет, то написать сообщение. Для этого в интерфейсе в функции turnOn добавим Output значение (boolean) result. Компилируем, сохраняем.
После этого в BP_lamp и BP_alert появилась ошибка, что ивент turnOn конфликтует.
Теперь обработчик события не просто ивент, а функция с собственным графом для логики. Он открывается двойным кликом по имени функции в блоке Interfaces справа. Добавим туда логику.
Также в BP триггера у ноды вызова функции turnOn появился возвращаемый параметр result, в котором будет значение, устанавливаемое в actor-объекте. Добавим печать предупреждения, если лампа не загорелась, или сигнализация не включилась. Так как у нас есть ссылка на сам объект, то мы можем взять из него любую информацию, например его координаты.
Теперь у нас есть контроль над всеми объектами с интерфейсом I_turnAbleItem, и мы можем локализовать все те из них, у которых что-то пошло не так.
Cast To
Еще одна важная функция для взаимодействия между BP это “Cast To <имя класса>”. Это функция приведения BP (класса) к типу, который мы указываем как параметр. Используя “Cast To”, можно вызвать кастомные функции actor-объектов, если мы не знаем заранее, какого типа actor-объект.
Например, добавим в BP_lamp обычную функцию setColor с параметром color
В персонаже добавим обработчик Trace, которые возвращает actor-объект типа Actor. У него нет функции setColor, которую мы сделали в лампе, поэтому, используя “Cast To” мы пробуем привести этот actor-объект к типу BP_lamp. По аналогии можо добавить каст к актору, который возвращет триггер при событиях пересечения.
И если этот actor-объект действительно типа BP_lamp, “Cast To” вернет actor-объект типа BP_lamp, и мы сможем у него вызывать функцию setColor. Если это какой-то другой BP, то просто ничего не делаем. Приведение к типу через Cas To также распространяется на Child классы. Если на сцене есть actor-объекты классов BP_spotLamp, BP_pointLight, то Cast To BP_lamp успешно приведет их к типу BP_lamp и вернет объект этого типа.
Туториал по Unreal Engine. Часть 2: Blueprints
Blueprints — это система визуального скриптинга Unreal Engine 4. Она является быстрым способом создания прототипов игр. Вместо построчного написания кода всё можно делать визуально: перетаскивать ноды (узлы), задавать их свойства в интерфейсе и соединять их «провода».
Кроме быстрого прототипирования, Blueprints также упрощают создание скриптов для непрограммистов.
В этой части туториала мы будем использовать Blueprints для следующих операций:
В этой части также используются векторы. Если вы с ними незнакомы, то рекомендую эту статью про векторы на gamedev.net.
Примечание: эта статья является одной из восьми частей туториала, посвящённого Unreal Engine:
Приступаем к работе
Скачайте начальный проект и распакуйте его. Чтобы открыть проект, перейдите в папку проекта и откройте BananaCollector.uproject.
Примечание: если откроется окно, сообщающее, что проект создан в более ранней версии Unreal editor, то всё в порядке (движок часто обновляется). Можно или выбрать опцию создания копии, или опцию преобразования самого проекта.
На рисунке ниже показана сцена. Именно в ней игрок будет перемещаться и собирать предметы.
Для простоты навигации я разбил файлы проекта на папки, как показано на рисунке:
Выделенную красным кнопку можно использовать, чтобы показать или скрыть панель исходников.
Создание игрока
В Content Browser перейдите к папке Blueprints. Нажмите на кнопку Add New и выберите Blueprint Class.
Мы хотим, чтобы актор получал вводимую игроком информацию, поэтому нам подходит класс Pawn. Выберите во всплывающем окне Pawn и назовите его BP_Player.
Примечание: класс Character тоже подойдёт. В нём даже по умолчанию есть компонент перемещения. Однако мы будем реализовывать собственную систему движения, поэтому класса Pawn нам достаточно.
Прикрепление камеры
Камера — это способ игрока смотреть на мир. Мы создадим камеру, смотрящую на игрока сверху вниз.
В Content Browser дважды нажмите на BP_Player, чтобы открыть его в Blueprint editor.
Для создания камеры перейдите на панель Components. Нажмите на Add Component и выберите Camera.
Чтобы камера смотрела сверху вниз, нужно расположить её над игроком. Выбрав компонент камеры, перейдите во вкладку Viewport.
Активируйте манипулятор перемещения, нажав клавишу W, а затем переместите камеру в (-1100, 0, 2000). Или же можно ввести координаты в поля Location. Она находится в разделе Transform панели Details.
Если вы потеряли камеру из виду, нажмите клавишу F, чтобы сфокусироваться на ней.
Затем активируйте манипулятор поворота, нажав клавишу E. Поверните камеру вниз на -60 градусов по оси Y.
Отображаем игрока
Мы обозначим персонажа игрока красным кубом, поэтому для его отображения нужно будет использовать компонент Static Mesh.
Во-первых, снимите выделение с компонента Camera, нажав левой клавишей мыша на пустом пространстве в панели Components. Если этого не сделать, то следующий добавленный компонент будет дочерним по отношению к камере.
Нажмите на Add Component и выберите Static Mesh.
Чтобы отобразить красный куб, выберите компонент Static Mesh, а затем перейдите во вкладку Details. Нажмите на раскрывающийся список, находящийся справа от Static Mesh и выберите SM_Cube.
Вы должны увидеть следующее (можно нажать F внутри Viewport, чтобы сфокусироваться на кубе, если вы его не видите):
Теперь настало время заспаунить актора Pawn игрока. Нажмите на Compile и вернитесь к основному редактору.
Спаун игрока
Чтобы игрок мог управлять Pawn, нужно указать две вещи:
Создание Game Mode
Класс Game Mode (игровой режим) — это класс, управляющий тем, как игрок входит в игру. Например, в многопользовательской игре Game Mode используется для задания спауна каждого игрока. Что более важно, Game Mode определяет. какой Pawn будет использовать игрок.
Перейдите к Content Browser и зайдите в папку Blueprints. Нажмите на кнопку Add New и выберите Blueprint Class.
Во всплывающем меню выберите Game Mode Base и назовите его GM_Tutorial.
Теперь нужно указать, какой класс Pawn будет использоваться по умолчанию. Дважды нажмите на GM_Tutorial, чтобы открыть его.
Перейдите на панель Details и загляните в раздел Classes. Нажмите на раскрывающийся список Default Pawn Class и выберите BP_Player.
Чтобы использовать новый Game Mode, нужно сообщить уровню, какой Game Mode он должен использовать. Это можно указать в World Settings. Нажмите на Compile и закройте Blueprint editor.
Каждый уровень имеет собственные параметры. Получить доступ к этим параметрам можно, выбрав Window\World Settings. Или же можно зайти в Toolbar и выбрать Settings\World Settings.
Рядом со вкладкой Details откроется новая вкладка World Settings. В ней нажмите на раскрывающийся список GameMode Override и выберите GM_Tutorial.
Теперь вы увидите, что классы сменились на те, которые выбраны в GM_Tutorial.
Наконец, нам нужно задать точку спауна игрока. Это реализуется размещением на уровне актора Player Start.
Размещение Player Start
В процессе спауна игрока Game Mode ищет актор Player Start. Если Game Mode находит его, то предпринимает попытку заспаунить игрока там.
Чтобы разместить Player Start, перейдите к панели Modes и найдите Player Start. Нажмите левой клавишей и перетащите Player Start из панели Modes во Viewport. Отпустите левую клавишу мыши, чтобы разместить его.
Можете разместить его где угодно. Когда закончите, перейдите в Toolbar и нажмите Play. Вы будете заспаунены в точке расположения Player Start.
Чтобы выйти из игры, нажмите кнопку Stop в Toolbar или нажмите клавишу Esc. Если вы не видите курсор, нажмите Shift+F1.
Это не похоже на игру, если мы не можем двигаться. Наша следующая задача — настроить параметры ввода.
Настройка ввода
Назначение клавиши действию называется привязкой клавиши.
В Unreal можно настроить привязки клавиш, чтобы при их нажатии срабатывали события. События — это ноды, выполняющиеся при определённых действиях (в этом случае — при нажатии указанной клавиши). При срабатывании события выполняются все ноды, соединённые с событием.
Такой способ привязки клавиш удобен, потому что он означает, что нам не нужно жёстко задавать клавиши в коде.
Если мы будем задавать клавиши жёстко, то нам придётся заходить в каждый актор и менять клавиши отдельно.
Привязка осей и действий
Чтобы перейти к параметрам ввода, зайдите в Edit\Project Settings. В разделе Engine выберите слева Input.
В разделе Bindings выполняется настройка ввода.
Unreal предоставляет два способа создания привязок клавиш:
Создание привязок движения
Во-первых, мы создадим две группы привязки осей. Группы позволяют привязывать несколько клавиш к одному событию.
Для создания новой группы привязки осей нажмите на значок + справа от Axis Mappings. Создайте две группы и назовите их MoveForward и MoveRight.
MoveForward будет управлять движением вперёд и назад. MoveRight будет управлять движением влево и вправо.
Мы привяжем движение к четырём клавишам: W, A, S и D. Пока у нас есть только два слота для привязки клавиш. Добавим к каждой группе ещё одну привязку осей, нажав на значок + рядом с полем имени группы.
Чтобы привязать клавишу, нажмите на раскрывающийся список с перечислением клавиш. Привяжите клавиши W и S к MoveForward. Привяжите клавиши A и D к MoveRight.
Теперь нужно задать значения в полях Scale.
Значение оси и масштаб ввода
Перед заданием полей Scale нам нужно больше узнать о том, как работать со значениями осей.
Мы можем использовать значение оси для управления скоростью Pawn. Например, если мы нажмём стик до упора, то значение оси будет 1. Если нажать наполовину, то значение будет 0.5.
Умножая значение оси на переменную скорости, мы можем регулировать с помощью стика скорость движения.
Также значение оси можно использовать для задания направления вдоль оси. Если умножить скорость Pawn на положительное значение оси, то мы получим положительное смещение. При использовании отрицательного значения оси получим отрицательное смещение. Прибавляя это смещение к местонахождению Pawn, мы задаём направление его движения.
Клавиши клавиатуры могут подавать на выход только значения 1 или 0, то можно использовать scale для преобразования их в отрицательные числа. Это можно сделать, взяв значение оси и умножив его на масштаб.
Умножив положительное (значение оси) на отрицательный (масштаб), мы получим отрицательное значение.
Задайте масштаб клавиш S и A, нажав на поле Scale и введя -1.
Теперь начинается интересное: заставим Pawn двигаться! Закройте Project Settings и откройте BP_Player в Blueprints editor, дважды нажав на него.
Перемещение игрока
Сначала нам нужно выбрать события для привязок движения. Нажмите правой клавишей мыши на пустом пространстве в Event Graph, чтобы открыть список нодов. Найдите в этом меню MoveForward. Добавьте нод MoveForward из списка Axis Events. Учтите, что вам нужен красный нод в Axis Events, а не зелёный нод в Axis Values.
Повторите процесс для MoveRight.
Теперь мы настроим ноды для MoveForward.
Использование переменных
Для перемещения необходимо указать, с какой скоростью будет двигаться Pawn. Один из простых способов указания скорости — хранение её в переменной.
Чтобы создать переменную, зайдите во вкладку My Blueprint и нажмите на значок + в правой части раздела Variables.
Выбрав новую переменную, перейдите во вкладку Details. Измените имя переменной на MaxSpeed. После этого замените тип переменной на Float. Для этого нужно нажать на раскрывающийся список рядом с Variable Type и выбрать Float.
Теперь необходимо задать значение по умолчанию. Но чтобы его задать, нужно будет нажать Compile в Toolbar.
Выбрав переменную, перейдите ко вкладке Details. Зайдите в раздел Default Value и измените значение MaxSpeed по умолчанию на 10.
Затем перетащите переменную MaxSpeed из вкладки My Blueprint на Event Graph. Выберите из меню пункт Get.
Теперь нужно умножить MaxSpeed на значение оси, чтобы получить конечную скорость и направление. Добавим нод float * float и присоединим к нему Axis Value и MaxSpeed.
Получение направления игрока
Чтобы двигаться вперёд, нам нужно знать, куда смотрит Pawn. К счастью, в Unreal есть для этого нод. Добавьте нод Get Actor Forward Vector.
Затем добавьте нод Add Movement Input. Этот нод получает направление и значение, преобразуя их в хранимое смещение. Соедините ноды следующим образом:
Белая линия обозначает цепочку выполнения. Другими словами, когда игрок перемещает ось ввода, то генерируется событие, выполняющее нод InputAxis MoveForward. Белая линия показывает, что после этого выполняется нод Add Movement Input.
Нод Add Movement Input получает на входе следующие данные:
Добавление смещения
Чтобы действительно двигать Pawn, нам нужно получить смещение, вычисленное Add Movement Input, и прибавить его к местоположению Pawn.
В сущности, наша стратегия будет заключаться в перемещении игрока на небольшую величину в каждом кадре игры, поэтому нам нужно добавить перемещение к событию Event Tick, которое генерируется каждый кадр.
Перейдите к ноду Event Tick в Event Graph. Он должен быть неактивным и находиться слева, но если его нет, то создайте нод самостоятельно.
Чтобы получить смещение, создадим нод Consume Movement Input Vector. Чтобы прибавить смещение, создадим нод AddActorLocalOffset. После этого соединим их следующим образом:
Это означает, что в каждом кадре игры мы будем сохранять весь ввод перемещения и прибавлять его к текущему местоположению актора.
Нажмите Compile, перейдите к основному редактору и нажмите на Play. Теперь вы можете двигаться в сцене!
Однако у нас есть небольшая проблема. Мощные компьютеры могут рендерить кадры с большей частотой. Event Tick вызывается каждый кадр, поэтому ноды перемещения будут выполняться чаще. Это значит, что Pawn будет двигаться на мощных компьютерах быстрее, и наоборот.
Чтобы решить эту проблему, наше движение должно быть независимым от частоты кадров.
Примечание: я настроил привязки клавиш, чтобы показать влияние зависимости от частоты кадров. Нажмите 0, чтобы ограничить частоту 60 кадрами в секунду, и нажмите 1, чтобы снять ограничение. Попробуйте перемещаться при обоих частотах кадров и вы заметите разницу в скорости.
Независимость от частоты кадров
Независимость от частоты кадров означает, что мы постоянно будем получать одинаковые результаты, вне зависимости от частоты кадров. К счастью, достичь такой независимости в Unreal очень просто.
Выйдите из игры, а затем откройте BP_Player. Затем перейдите к узлу Event Tick и посмотрите на Delta Seconds.
Delta Seconds — это величина времени, прошедшего после предыдущего Event Tick. Умножив смещение на Delta Seconds, мы сделаем перемещение независимым от частоты кадров.
Например, наш Pawn имеет максимальную скорость 100. Если после предыдущего Event Tick прошла одна секунда, то Pawn переместится на полные 100 единиц. Если прошли полсекунды, то он переместится на 50 единиц.
Если движение зависимо от частоты кадров, то Pawn будет перемещаться на 100 единиц в каждом кадре, вне зависимости от времени между кадрами.
Чтобы умножить смещение на Delta Seconds, добавьте нод vector * float. После этого соедините ноды следующим образом:
Время между кадрами (Delta Seconds) очень мало, поэтому Pawn будет двигаться намного медленнее. Это можно исправить, заменив значение MaxSpeed по умолчанию на на 600.
Поздравляю вам удалось добиться независимости от частоты кадров!
Можно заметить, что куб проходить сквозь все объекты. Чтобы исправить это, нам нужно познакомиться с коллизиями.
Надевайте шлем, потому что сейчас нам придётся столкнуться с теорией!
Коллизии актора
Когда мы вспоминаем о столкновениях, то представляем автомобильные аварии. К счастью, коллизии в Unreal намного безопаснее.
Чтобы иметь возможность сталкиваться с объектами, актору нужно обозначение его пространства столкновений (обычно называемого коллизией). Можно использовать одно из следующих пространств:
Коллизия происходит, когда коллизия актора касается коллизии другого актора.
Теперь настало время включить коллизии.
Включение коллизий
Вы, наверно, недоумеваете, почему куб не сталкивается с объектами, хотя у него есть меш коллизии. При перемещении актора Unreal учитывает для коллизий только корневой компонент. Поскольку корневой компонент Pawn не имеет коллизии, он проходит сквозь все объекты.
Примечание: актор, не имеющий коллизии в корневом компоненте, всё равно может блокировать других акторов. Но если перемещать актора, то он не будет ни с чем сталкиваться.
Итак, чтобы использовать меш коллизии, StaticMesh должен быть корневым. Для этого перейдите в панель Components. Затем зажмите левую клавишу мыши и перетащите StaticMesh на DefaultSceneRoot. Отпустите левую клавишу мыши, чтобы сделать StaticMesh новым корневым компонентом.
Чтобы коллизии начали работать, нужно выполнить ещё одно действие. Переключитесь на Event Graph и перейдите к узлу AddActorLocalOffset. Найдите вход Sweep и измените значение на true, нажав левой клавишей мыши на флажок.
AddActorLocalOffset занимается тем, что телепортирует актора в новое место. Sweep гарантирует, что актор будет сталкиваться со всем, что находится между старым и новым местоположением.
Перейдите в основной редактор и нажмите на Play. Теперь куб будет реагировать на коллизии с уровнем!
Последнее, что мы создадим — это предмет, исчезающий при контакте с персонажем игрока.
Создание предмета
В общем случае предметом является любой собираемый игроком объект. Мы используем в качестве предмета BP_Banana.
Чтобы распознать контакт куба и предмета, нам нужен нод события, срабатывающего при коллизии. Для генерирования таких событий можно использовать реакции на коллизи.
Реакция на коллизию также определяет, как актор реагирует на коллизию с другим актором. Существует три типа реакций на коллизии: Ignore, Overlap и Block. Вот как они взаимодействуют друг с другом:
Хотя здесь можно использовать и Overlap, и Block, в этом туториале мы будем использовать только Overlap.
Задание реакции на коллизию
Закройте игру и откройте BP_Banana. Выберите компонент StaticMesh, а затем перейдите в панель Details. Реакции на коллизии задаются в разделе Collision.
Как вы видите, большинство параметров неактивно. Чтобы сделать их изменяемыми, нажмите на раскрывающийся список рядом с Collision Presets. Выберите в списке Custom.
Теперь нам нужно указать реакцию на коллизию между предметом и кубом.
Компоненты имеют атрибут под названием object type (тип объекта). Тип объекта — это просто удобный способ группировки похожих акторов. Подробнее о типах объектов можно прочитать здесь.
Куб имеет тип WorldDynamic, поэтому нам нужно изменить реакцию на коллизию этого типа. В разделе Collision Responses измените реакцию на коллизию WorldDynamic на Overlap. Это можно сделать, нажав на средний флажок справа от WorldDynamic.
Обработка коллизий
Для обработки коллизий нужно использовать событие наложения. Перейдите в панель Components и нажмите правой клавишей мыши на StaticMesh. В контекстном меню выберите Add Event\Add OnComponentBeginOverlap.
Так мы добавим в Event Graph нод OnComponentBeginOverlap (StaticMesh).
Наконец, создадим нод DestroyActor и соедините его с нодом OnComponentBeginOverlap (StaticMesh). Как можно догадаться по названию, он удаляет целевой актор из игры. Однако поскольку целевого актора нет, он уничтожит актор, вызвавший его.
Размещение предмета
Закройте Blueprint editor и перейдите в папку Blueprints.
Начните располагать бананы на уровне зажав левую клавишу мыши и перетаскивая BP_Banana во Viewport.
Нажмите Play и начните собирать бананы!
Куда двигаться дальше?
Готовый проект можно скачать отсюда.
Вы сделали ещё один шаг к тому, чтобы стать специалистом по Unreal Engine.
Если вы хотите продолжить обучение, то прочитайте следующий пост в серии, где мы подробнее рассмотрим материалы Unreal Engine.
















