Статья предназначена для новичков в 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.

image

В интерфейсе добавим функции turnOn и turnOff. Также мы хотим, чтобы можно было регулировать величину яркости или громкости соответствующих actors-объектов. Добавим функцию setValue, и в ней параметр (float) value. Функции в интерфейсе выглядит неактивными, и в рабочей области ничего нельзя делать. Это потому, что тут задается только само название функций и их параметры, а реализация логики будет уже в самом BP.

image

Теперь можно скомпилировать, сохранить и закрыть интерфейс.

Открываем BP bp_lamp и bp_alarm, переходим в настройку и добавляем интерфейс I_turnAbleItem и компилируем BP.

image

Тем самым мы указали, что в этих BP есть обработчики событий turnOn, turnOff, setValue, и можем задать функционал для этих событий.

Теперь если в Event Graph вызвать контекстное меню и вписать turnOn, высветятся три строки в разных категориях:

Add Event — добавить обработчик события. Именно это используется для выполнение логики при активации из другого BP:

  • Call Function — это активация собственного ивента, описанного выше.
  • Class — с помощью этой функции можно вызвать ивент turnOn у другого BP с интерфейсом.
  • I_turnAbleItem, этот BP указывается параметром в функции.

image

Здесь нам нужен Add Event, добавляем все 3 ивента, описанных в интерфейсе, и описываем логику, которая должна выполняться при их вызове.

image

Наши BP готовы. В качестве активатора создадим простой BP_Button с триггером. Если персонаж вошел в область триггера — включаем лампы и сигнализации, ставим им value = 10. Когда выходит — выключаем. Если в сцене уже установлены конкретные лампы и алерты, то можно их задать в actors-объекте BP_Button как переменные или массив объектов, но что делать, если таких объектов несколько сотен, или они генерируются динамически, и до запуска игры их нет на сцене? Для этого можно применить функцию GetActorWithInterface. Она вернет массив всех объектов на сцене, у которых есть заданный интерфейс. Используем эту функцию, в качестве интерфейса выберем I_turnAbleItem. В цикле по полученному массиву объектов у каждого из них вызовем ивент turnOn.

image

Нода turnOn будет показана со знаком конверта (message), обозначающим, что это вызов функции интерфейса. Так же добавляем вызов setValue. В нем будет возможность поставить значение value, которое мы указали в интерфейсе. По аналогии, на событие endOverlap ставим turnOff. Результат будет выглядеть примерно так:

image

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

Преимущество вызовов функций интерфейс через message еще в том, что их можно попытаться вызвать у любого BP, даже если у него нет интерфейса с вызываемой функцией, в таком случае просто ничего не произойдет. Например, мы получили ссылку на некий actor-объект в результате Trace и не знаем, что это за actor-объект, но мы хотим, чтобы он мог показать какой-то текст при наличии функции показа текста. Для таких случаев можно использовать интерфейс вызов (message). Если у actor-объекта будет вызываемый обработчик, он сработает.

Усложним задачу. Нам надо убедится, что лампа включилась, а если нет, то написать сообщение. Для этого в интерфейсе в функции turnOn добавим Output значение (boolean) result. Компилируем, сохраняем.

image

После этого в BP_lamp и BP_alert появилась ошибка, что ивент turnOn конфликтует.

image

Теперь обработчик события не просто ивент, а функция с собственным графом для логики. Он открывается двойным кликом по имени функции в блоке Interfaces справа. Добавим туда логику.

image

Также в BP триггера у ноды вызова функции turnOn появился возвращаемый параметр result, в котором будет значение, устанавливаемое в actor-объекте. Добавим печать предупреждения, если лампа не загорелась, или сигнализация не включилась. Так как у нас есть ссылка на сам объект, то мы можем взять из него любую информацию, например его координаты.

image

Теперь у нас есть контроль над всеми объектами с интерфейсом I_turnAbleItem, и мы можем локализовать все те из них, у которых что-то пошло не так.

Cast To


Еще одна важная функция для взаимодействия между BP это “Cast To {имя класса}”. Это функция приведения BP (класса) к типу, который мы указываем как параметр. Используя “Cast To”, можно вызвать кастомные функции actor-объектов, если мы не знаем заранее, какого типа actor-объект.

Например, добавим в BP_lamp обычную функцию setColor с параметром color

image

В персонаже добавим обработчик Trace, которые возвращает actor-объект типа Actor. У него нет функции setColor, которую мы сделали в лампе, поэтому, используя “Cast To” мы пробуем привести этот actor-объект к типу BP_lamp.

image

И если этот actor-объект действительно типа BP_lamp, “Cast To” вернет actor-объект типа BP_lamp, и мы сможем у него вызывать функцию setColor. Если это какой-то другой BP, то просто ничего не делаем. Приведение к типу через Cas To также распространяется на Child классы. Если на сцене есть actor-объекты классов BP_spotLamp, BP_pointLight, то Cast To BP_lamp успешно приведет их к типу BP_lamp и вернет объект этого типа.

image

Спасибо за внимание!
Поделиться с друзьями
-->

Комментарии (9)


  1. ufna
    23.01.2017 18:12

    Было бы очень здорово, если бы вы ещё написали чем технически отличаются касты к интерфейсу и к классу, какие "последствия" несёт за собой каждый случай, и почему использование интерфейсов в целом более предпочтительно для интерфейса и базовых классов, а в рамках экторов на сцене — в общем случае каст будет быстрее и проще ;)


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


  1. lookid
    23.01.2017 20:28

    Внутри dynamic_cast или static_cast?


    1. zikoko
      23.01.2017 23:10

      Внутри своя функция каста, написанная Эпиками
      Cast(ReferenceToCast);

      Но принцип как dynamic


  1. Alex_ME
    23.01.2017 21:58

    Возможно оффтоп, но спрошу. Когда я немного знакомился с UE4 меня сильно запутала эта двойственность — Blueprints и код. С учетом большого количества материалов по Blueprints и куда меньшего по обычному коду, это вызывает затруднения.

    А вообще, что предпочтительнее использовать в разных задачах в UE4? Есть ли какие-нибудь best practice, чтобы не изобретать велосипеды и не ходить по давно пройденным граблям?


    1. saturlag
      23.01.2017 23:24

      Сейчас вроде по всему много документации.
      Хотя БП позиционированы как инструмент для прототипирования, сами Эпики говорят, что БП можно для всего использовать, они практически не теряют производительности, разве что стараться не ставить много логики в тике. Хотя код на плюсах все-таки стабильнее работает, БП иногда бывает подглючивают, и не совсем все есть в БП.
      Можно спокойно совмещать, использовать свои с++ классы как ноды в БП. Что-то проще делать на плюсах, что-то на БП.


    1. netgoblin
      24.01.2017 01:30

      Код на БП можно портировать на любую платформу. Это мега-плюс.


      1. Flakky
        25.01.2017 15:37

        Так и плюсовый вроде можно, если не выходить за рамки стандартного API.


    1. zikoko
      24.01.2017 12:09

      Там где я работаю, принято базу писать на С++, а остальное можно на бп
      Еще добавлю, что когда проект, который пишут на БП разрастается, он начинает подвисать, и становится жутко неудобно работать.


      1. Flakky
        25.01.2017 15:41

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

        В остальном вы правы. Главное задать основу на плюсах (больше актуально для онлайновых или технически-сложных игр), а дальше уже в дочерних дописывать БП. Хотя большинство сингловых жанров, вроде шутера, можно писать и полностью на БП.