GameDev 3.0. Этап 2.1. Дальний бой. Урок 5. Взаимодействие с кнопкой
Стартовая страница
- Добавляем 3D-кнопки, импорт через формат GLTF, исправление размера и pivot-point
- Загружаем модель дверей и ее анимацию, настраиваем коллизию, проигрываем анимацию
- Создаем блюпринт для кнопки и настраиваем механику взаимодействия
- Анимация персонажа
- Поворот и передвижение персонажа к кнопке
Добавляем 3D-кнопки, импорт через формат GLTF, исправление размера и pivot-point
Найдем для начала кнопку на скетчфабе.
Загрузим в UE кнопку на примере формата GLTF. Для этого в плагинах надо подключить glTF Importer.
Далее, создаем папку Content -> Systems -> Interaction -> Assets и туда загрузим кнопку. Далее нажимаем кнопку как на картинке снизу:
Укажем путь, куда сохранить ассет и выбираем файл. Загружаем, ставим на место куда нам надо, скейлим если надо.
Если пивот смещен, то исправим это в Modeling Mode с помощью инструмента Pivot.
Выбираем этот инструмент, выделяем объект, где надо исправить пивот. Можно вручную поставить, можно в центр, вниз и т.д., после выставления нажимаем на кнопку Accept.
Обратно переходим в Selection Mode.
↑ Вверх
Загружаем модель дверей и ее анимацию, настраиваем коллизию, проигрываем анимацию
Теперь загрузим дверь, которая будет открываться с помощью этой кнопки.
Для начала перенесем в UE подготовленную дверь, потом заменим. Перетянем в папку Content папку с дверью. Отсюда перекинем в папку Content -> Systems -> Interaction -> Assets и Animation Sequence перекинем в нашу сцену.
Отключим Looping в Details, чтобы не проигрывалась постоянно:
Сейчас у двери есть коллизия, удалим все это и настроим заново коллизию. Для этого заходим в файл c PhysicsAsset, выделяем все сферы и удалим
И теперь в Primitive Type выбираем Box
И нажимаем Generate All Bodies. Оставляем коллизию для door_root, а остальное удалим. Подправим, повернем чуть-чуть. Сохраняем и закрываем.
Теперь коллизия есть именно возле двери, а не как раньше. Но мы не можем проходить, чтобы пройти, будем выключать коллизию в нужный момент.
Теперь заставим все это работать друг с другом, т.е. при нажатии на кнопку дверь открывалась.
↑ Вверх
Создаем блюпринт для кнопки и настраиваем механику взаимодействия
Для начала создадим блюпринт кнопки. Создадим папку BP в Interactions. В этой папке нажимаем ПКМ -> Blueprint Class, выбираем Actor.
Все блюпринты называем с префиксом BP_ , это для нас, чтобы классифицировать.
Назовем наш блюпринт BP_Button. Заходим внутрь. Для начала добавим кнопку. Нажимаем кнопку +Add, в поиске пишем Static Mesh и выбираем его и назовем его Button. И теперь сюда мы можем закинуть любой 3D-объект. Закинем нашу кнопку.
Переименуем нашу кнопку для удобства в ButtonMesh. Выделяем его в Content Browser и переходим в блюпринт. Справа в Static Mesh нажимаем эту кнопку и наша кнопка загрузится в блюпринт.
Теперь мы можем повесить на нашу кнопку любой функционал.
Для начала зайдем в блюпринт кнопки и добавим компонент Sphere Collision.
Эта сфера нужна нам для того, чтобы определять, что наш персонаж вошел в нее и может активировать эту кнопку. Увеличим ее радиус до 240.
Подправим сферу, чтобы персонаж случайно не взаимодействовал с этой кнопкой, находясь по другую сторону стены. Для этого выделим кнопку, найдем эту сферу и подвинем ее.
Теперь нам нужно определить, что в эту сферу вошел наш персонаж. Для этого заходим в блюпринт и добавим событие как на картинке - Add OnComponentBeginOverlap.
То есть когда с ним пересекаются, мы проведем какую-то логику.
И еще нам нужен ивент, когда мы перестали пересекать эту сферу, то есть ушли - Add OnComponentEndOverlap.
Нам надо определить, что именно наш персонаж пересек. Для этого в Begin Overlap в Other Actor выведем Cast To BP_ThirdPersonCharacter.
Эта логика работает следующим образом: мы проверяем кто вошел в сферу, если не наш персонаж, то ничего не случится, пойдет по сценарию Cast Failed, а если наш персонаж, то логика пойдет по верхней ветке.
Для End Overlap добавим такую же ноду:
Теперь запомним нашего персонажа, из As BP Third Person Character выведем Promote to Variable и назовем эту переменную CharRef
Также добавим новую переменную CanUse? тип Boolean. И вытягиваем с зажатым Alt после переменной и ставим галочку. А снизу выключим.
Теперь передаем управление этой кнопке. Для этого вызовем ноду Get Player Controller, отсюда вытянем ноду Enable Input, но Get Player Controller подключим не к Target, а к Player Controller, а сам Enable Input подключим к переменной CanUse?
Снизу тоже вызываем Get Player Controller, но пишем теперь Disable Input, подключим как на картинке, и забудем перса CharRef. Когда мы хотим забыть переменную, например, мы можем вызвать эту переменную и не указывать ей ничего.
Итак, мы включили главную логику. Теперь чуть ниже вызываем клавишу Е, хотим, чтобы при нажатии на Е активировалось нажатие на кнопку. Вызовем после Е проверку Branch, в Condition подключаем нашу переменную CanUse?
Далее на всякий случай делаем проверку нашего персонажа, записана ли какая-нибудь переменная. Для этого с зажатым Ctrl вытягиваем CharRef и из него вытягиваем ноду Is Valid и именно со знаком вопроса.
Теперь, если наш перс записан, то идет по пути Is Valid, если нет - то Is Not Valid.
Добавляем еще одну переменную Interacting? - она нужна для того, чтобы избавиться от багов в будущем, когда мы уже активировали эту кнопку и не смогли повторно активировать еще много раз.
Вытягиваем эту переменную с Ctrl, из него вытянем Branch, а из него из False переменная Interacting? c зажатым Alt и на False мы его включаем.
Теперь у нас выходит False. А на True ничего не делаем.
Теперь мы должны отключить управление нашего персонажа, чтобы мы ек могли ничего не делать - ни бегать, ни прыгать и т.д., чтобы управление персонажем полностью передалось на кнопку.
Для этого достаем ноды Get Player Controller и Disable Input, можем просто скопировать их, чтобы не писать еще раз. А к Target подключим нашего персонажа CharRef.
Проверим, запустим игру, если возле кнопки на персонажа не действуют бег, прыжок и т.д., значит все сделали правильно.
Дальше ввызовем ноду Sequence, так как вызовем несколько функций, которые разделим для удобства.
↑ Вверх
Анимация персонажа
Теперь нам понадобится анимация для нашего персонажа - анимация нажатия кнопки. В папке материалов эта анимация есть. В будущем ее легко можно заменить на другую. Закидываем эту анимацию в папку Content. Дальше перенесем эту анимацию в папку Content -> Systems -> Interaction -> Assets и нажимаем Move Here.
Теперь для этой анимации нам нужно создать AnimMontage - это такая же анимация, единственное, мы можем ее удобно проигрывать в любой момент в любой механике. Это нужно для того, чтобы не возвращаться в ABP_Manny, не делать там анимацию для персонажа, потому что там мы делаем только основные анимации, такие как Idle, Run, Walk, передвижение с оружием и т.д.
А обычные действия, такие как нажатие на кнопку, рычаг, поднятие чего-либо, это делается с помощью AnimMontage, потому что так удобней.
Это многофункциональный инструмент для редактирования анимации. Он может подключать несколько анимаций и поддерживает пользовательскую сегментацию анимационных сегментов, воспроизведение петель, произвольный переход и взаимодействие со слоистой анимацией для реализации функции разных костей для воспроизведения разных анимаций. Он поддерживает редактирование уведомлений и кривой как обычная последовательность анимации. В то же время, Animation Montage также поддерживает повторное использование.
На анимации нажатия на кнопку нажимаем ПКМ -> Create -> Create AnimMontage.
AnimMontage обычно называют с префиксом AM_ и дальше название самой анимации. Назовем нашу анимацию AM_PressButton.
Заходим в блюпринт нашей кнопки, находим место с сиквенсом (чуть выше делали) и ищем ноду Delay (делает небольшую задержку на заданное время), подключаем ее к сиквенсу Then 1. Пока поставим на 1 секунду.
После Delay нам надо проиграть анимацию, но просто так вызвать мы не можем, нам нужен CharRef, вытянем его с зажатым Ctrl, и уже отсюда пишем Play Anim Montage. CharRef должен быть в Target. Подключаем Play Anim Montage с Delay.
Выберем анимацию, которую надо проиграть:
↑ Вверх
Поворот и передвижение персонажа к кнопке
Теперь нам надо повернуть персонажа к кнопке, чтобы он смотрел на нее.
Для этого создадим новую функцию, добавим ноду Add Timeline, назовем ее RotateCharacter. Подключаем ее к сиквенсу в Then 1, так же вынесем с зажатым Ctrl переменную CharRef, из нее вызовем ноду Set Actor Rotation. И подключаем к Update - это важно!
Пока что RotateCharacter ничего не делает. Зайдем внутрь, нажимаем +Track -> Add Float Track
Добавится график, назовем его RotationAlpha:
Здесь с зажатым Shift можем создавать точки, создадим 2 точки, Length ставим 0.4 секунды, то есть эта анимация будет длиться 0,4 секунды, 400 миллисекунд:
Выделяем первую точку и в Time ставим 0, в Value тоже ставим 0:
У нас получился линейный график, сделаем немного плавным, чтобы в начале резко была, а в конце медленной. Для этого выделим обе точки - ПКМ -> User, потянем за усики и настроим так, как нам надо.
Возвращаемся в Event Graph, теперь у нас в RotateCharacter появляется вот это значение:
Из нее вызываем ноду Lerp, позволяет одно число изменить на другое с плавностью, усик перенесем в Alpha:
New Rotation в Set Actor Rotation разделим на три значения с помощью Split Struct Pin:
И то, что получается в Lerp это у нас будет New Rotation Z (Yaw) (Rotation Z - по этой оси как раз нам и надо поворачивать персонажа к кнопке):
Из CharRef вызовем Get Actor Rotation, также разделим на 3 и Return Value Z соединим с Lerp -> A:
То есть мы из начального положения персонажа А переходим в положение В, которое сейчас и вычислим.
Вызовем ноду Get Actor Location, скопируем ее и переменную CharRef соединяем с таргетом верхнего Get Actor Location.
С верхней ноды вызываем ноду Find Look at Rotation, где Start - это наш персонаж, а Target - это наша кнопка (вот тут и вычисляется угол поворота), из нее выведем Break Rotator и Z соединим с Lerp -> B.
Теперь, когда нажимаем клавишу Е, наш персонаж повернется к кнопке.
При проверке есть баг, в будущем пофиксим.
Сейчас добавим функцию, когда перед нажатием перс подбежит к кнопке.
Для этого заходим в блюпринт перса BP_ThirdPersonCharacter и создадим специальный ивент.
ПКМ на пустом месте, пишем Т., когда пишем так, быстро находится Add Custom Event...
Назовем этот ивент MoveToLocation, теперь вызовем Get Player Controller, из нее вызовем ноду Simple Move To Location. Из Goal проведем ветку до ивента, пока не появится надпись Add Pin To Node и отпускаем
Эта функция заставит бежать персонажа к определенной точке, которую мы укажем в этом ивенте. Сейчас она не делает вообще ничего, пока мы сами не укажем использовать этот ивент в каком-то месте.
Прежде всего, добавим кое-какой объект. В Place Actors пишем Nav Mesh и находим NavMeshBoundsVolume
Вытягиваем его на карту и нажимаем клавишу P, и должна прогрузиться зеленая область именно на земле:
Эта функция нужна будет нам еще для нашего искусственного интеллекта и, в целом, для функции нашего персонажа. Она позволяет перемещаться ИИ в этой области, поэтому сейчас увеличим эту область, потому что нужна побольше.
Теперь вернемся в BP_Button и на новый Then повесим перемещение нашего персонажа. Для этого вызываем CharRef с зажатым Ctrl, из него вызываем MoveToLocation, тот ивент, который мы создали ранее. И укажем, куда будет персонаж идти. И MoveToLocation подключаем на новый Then.
Мы должны подойти прям к кнопке, но чуть подальше от нее. Чтобы вычислить правильную локацию, берем ноду Get Actor Location (скопируем рядом находящийся), подключим к нему "-" (Subtract), и сюда еще подключим кое-какую функцию.
С Find Look Rotation выведем Get Forward Vector (он показывает условное направление):
И то, что у нас получается, умножаем (Multiply), но поменяем этот оператор. ПКМ -> To Float (single-precision):
Укажем значение 25 и получим такое:
И теперь берем локацию кнопки и отнимаем от нее эту величину, вектор. И вот что у нас получится
Проверим и видим, что сейчас наш персонаж скользит к кнопке. Поправим эти баги. Зайдем в блюпринт нашего персонажа BP_ThirdPersonCharacter и создадим новую переменную Stop? типа Boolean, она будет указывать, когда персонаж должен стоять, не двигается, и мы должны фиксить всякие баги.
Теперь возвращаемся в блюпринт кнопки BP_Button на логику клавиши Е, перед логикой Move to Location вызываем CharRef с зажатым Ctrl, вызываем из него ноду Set Stop? и отмечаем галочкой. Усик с зажатым Ctrl перенесем с Move to Location на Set Stop?:
Теперь мы включаем переменную Stop?, но она пока ничего не делает, потому что не указана логика, где эта переменная задействована.
Теперь укажем в блюпринте персонажа BP_ThirdPersonCharacter. Для начала наш персонаж очень странно поворачивается, это происходит потому что, логика поворачивания на месте продолжает работать.
Чтобы исправить, вызываем Stop? с зажатым Ctrl, вызываем Branch (B + ЛКМ), теперь подключим эту логику - с зажатым Ctrl перенесем усик с Limit Rotation на Branch, в Condition подключаем Stop? и False включим на Limit Rotation.
Теперь в Movement Input введем изменения (ниже - как выглядит):
Добавим ноду Select Float, подключим ее к Scale Value, в Peak A подключаем Stop? В общем, как на картинке снизу:
Эти ивенты отвечают за передвижение персонажа. Не двигается персонаж - значение 0, двигается - значение 1, назад, то -1, так же влево и вправо.
Теперь исправим анимацию. Зайдем в ABP_Manny, здесь надо внести кое-какую правку в функцию. Есть функция Should Move
Уберем выделенное и соединим напрямую вот так:
Теперь баг исчез, но появился новый, исправим и это.
В ABP_Manny в Event Graph найдем наши функции RotateR и RotateL:
Перед Set вызовем Branch, на False подключим эти же параметры, а на True прокопируем 2 Set'а и галочки должны быть выключены:
В Condition подключим: с зажатым Ctrl вытащим CharacterRef, из него пишем Get Stop? и подключаем ее в Condition:
И теперь, если у нас включен Stop?, т.е. нам нужно стоять, наш персонаж не должен поворачиваться. Проверим, запустим игру.
Есть небольшие косячки, это исправим далее позже.