[Из песочницы] Взаимодействие между Blue Print (Interface, Cast To)
Немного теории
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:
- Call Function — это активация собственного ивента, описанного выше.
- Class — с помощью этой функции можно вызвать ивент turnOn у другого BP с интерфейсом.
- I_turnAbleItem, этот 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 и вернет объект этого типа.
Спасибо за внимание!
Комментарии (1)
23 января 2017 в 18:12
0↑
↓
Было бы очень здорово, если бы вы ещё написали чем технически отличаются касты к интерфейсу и к классу, какие «последствия» несёт за собой каждый случай, и почему использование интерфейсов в целом более предпочтительно для интерфейса и базовых классов, а в рамках экторов на сцене — в общем случае каст будет быстрее и проще;)
Потому что эти два механизма существуют не просто так и их нельзя назвать эквивалентными.