Использование делегатов в UE4

Делегаты в С++ используются в ряде случаев. В этом туториале мы поговорим о делегатах в качестве указателей, которые указывают на специфические функции. Через делегаты мы можем вызывать функции на которые указывает делегат, когда происходит какое то действие.
Например, в предыдущем туториале, чтобы гененрировать события перекрытия (overlap events) через С++ мы использовали следующие строки кода:

Code


OnActorBeginOverlap.AddDynamic(this, &ACustomTriggerBox::OnTriggerEnter);
OnActorEndOverlap.AddDynamic(this, &ACustomTriggerBox::OnTriggerExit);

[свернуть]

Можно сказать, что OnActorBeginOverlap и OnActorAndOverlap являются делегатами. Когда актор пересекается с другим актором, делегат OnActorBeginOverlap сработает, и если он направлен на выполнение функции последует вызов и исполнение этой функции.

Understanding “valid function” and “bound”

Перед тем как начать создавать делегаты, давайте разъясним что такое «valid function» и «bound». Итак, каждый делегат принимает функции с определенными аргументами. Аргументы функции представляют собой две вещи:
— Возвращаемый тип (ie void, int32, float)
— Лист параметров

Например следующие функции, имеют разные аргументы:
void MyFunction() — в данном случае функция ничего не возвращает а потому не имеет и аргументов
int32 FunctionWithReturnType() — в этом случае тип возвращаемого значения int32 и функция не имеет аргументов
void DoSomething(float x,int32 y) – в этом случае тип функция ничего не возвращает, но имеет два параметра float и int32

Обратите внимание, что есть типы которые включены в список параметров. Например следующие функции имеют различные аргументы:
void DoSomething(float x,int32 y)
void DoSomething(int32 x,float y)

В этом уроке мы разберем 2 типа делегатов:
— Делегаты, которые принимают только одну функцию (single-cast delegates)
— Делегаты, которые принимают более одной функции (multi-cast delegates)

Связанные функции (bound function), это функции которые подключены к делегату, и выполняются при его срабатывании. Это означает, что если делегат имеет три функции связанные с ним. То когда мы вызовем его, будут срабатывать все три функции в обратном порядке их добавления (позже в этом уроке, мы рассмотрим как добавлять такие функции и вызывать их из делегата). Допустим что мы добавили следующие три функции делегата:
void MyFunction()
void MySecondFunction()
void MyThirdFunction()

Если мы скажем делегату выполнить функции, то выполнение будет в таком порядке:
void MyThirdFunction()
void MySecondFunction()
void MyFunction()

Теперь мы знаем все чтобы начать создавать собственные делегаты. Давайте начнем!

Creating delegates

Чтобы объявить делегат в UE4 мы используем соответствующий макрос для наших нужд. Используйте приложенную таблицу (взято с официальной страницы документации), чтобы найти необходимый макрос, чтобы использовать его для объявления делегата:

Теперь приступим к делу:
— Создаем С++ класс базированный на Actor классе
— Создаем BP на основе нашего класса
— Размещаем наш BP на уровне
В .h файле, сразу после GENERATED_BODY(), добавляем следующие макросы:

Code


/*Single-cast delegate declaration. No parameters*/
DECLARE_DELEGATE(MyDelegate)

/*Single-cast delegate declaration. One int32 parameter*/
DECLARE_DELEGATE_OneParam(MyIntDelegate, int32)

/*Multi-cast delegate declartion. One int32 parameter*/
DECLARE_MULTICAST_DELEGATE_OneParam(MyIntMulticastDelegate, int32)

/*Function with one int32 parameter*/
UFUNCTION()
void IntFunction(int32 x);

/*Function with one int32 parameter*/
UFUNCTION()
void SecondIntFunction(int32 x);

/*Function with one int32 parameter*/
UFUNCTION()
void ThirdIntFunction(int32 x);

/*Function with no parameters*/
UFUNCTION()
void SomeFunction();

[свернуть]
Убедитесь что ваши функции помечены с помощью UFUNCTION. В противном случае ваша игра вылетит, когда вы попытаетесь обратиться к ним через делегат.
Теперь перейдем в .cpp файл и добавим в него следующие реализации:
Code


void ADelegateActor::IntFunction(int32 x)
{
GLog->Log("Output from IntFunction: " + FString::FromInt(x));
}

void ADelegateActor::SecondIntFunction(int32 x)
{
GLog->Log("Output from SecondIntFunction: " + FString::FromInt(x*2));
}

void ADelegateActor::ThirdIntFunction(int32 x)
{
//x to square
float power = FMath::Pow(x, 2);
GLog->Log("Third Int Function: "+FString::SanitizeFloat(power));
}

void ADelegateActor::SomeFunction()
{
GLog->Log("Some function log");
}

[свернуть]
Теперь когда мы имеем объявления и реализации всех функций, ищем функцию BeginPlay и сразу после Super::BeginPlay() добавляем следующий код:
Code


//Declaring a delegate of MyDelegate type
MyDelegate MyDel;
//Binding a UFUNCTION to MyDel - this will not call the function just yet.
MyDel.BindUFunction(this, FName("SomeFunction"));

//Calling the bound function of the delegate
MyDel.Execute();

//Declaring a delegate of MyIntDelegate type
MyIntDelegate IntDelegate;

//Binding two UFUNCTIONs to IntDelegate - this will not call any function just yet
IntDelegate.BindUFunction(this, FName("IntFunction"));
IntDelegate.BindUFunction(this, FName("SecondIntFunction"));

//Since the IntDelegate is a single-cast delegate it will only contain up to one function
//In this case, the IntDelegate contains the last bound function
IntDelegate.Execute(50);

//Declaring a delegate of MyIntMulticastDelegate type
MyIntMulticastDelegate Del;

//Adding three UFUNCTIONs to the delegate - this will not call any function just yet
Del.AddUFunction(this, FName("IntFunction"));
Del.AddUFunction(this, FName("SecondIntFunction"));
Del.AddUFunction(this, FName("ThirdIntFunction"));

//Calling all the bound functions with a value of 10
Del.Broadcast(10);

[свернуть]
Обратите внимание на строки 18 и 29. Мы выполняем функции, с установленными значениями. Я решил назвать IntDelegate со значением 50 и Del со значением 10. Это цифры просто вертелись в моей голове. Не стесняйтесь расширять этот код как вам угодно.
Теперь, если вы скомпилируете и запустите свой код с теми же заданными значениями, вы получите следующий результат в логе:

Очень важная вещь, которая приходит мне на ум. Убедитесь, что ваши делегаты связаны с функциями,прежде чем делать их вызов. Для того чтобы это сделать, делегаты имеют следующую функцию:
IsBound(), которая возвращает true, если какая либо функция связана с делегатом.
Кроме того, имейте в виду, что:
Для вызова single-cast делегатов, используйте функцию Execute(<необязательные параметры>)
Для вызова multi-cast делегатов, используйте функцию Broadcast(<необязательные параметры>)

Для более подробной информации о делегатах, не забудьте посетить страницу документации.
Original page


Читайте также: