[Из песочницы] Взаимодействие между 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.

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

Спасибо за внимание!

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

  • 23 января 2017 в 18:12

    0

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


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

© Habrahabr.ru