keywords: [UE4]Event, Delegate, Multi-cast Related

keywords:UE4, BindRaw, BindUFunction, BindUObject, Pass Arguments, Variable

Examples

Delegate示例(BindUFunction传递参数)

示例1:

void UMyClass::FunctionWithVar(const FString& MyVar, TFunction<void(const FString&)> InFunction)
{
    MyLambdaHandle = OnMyDelegate.BindStatic([MyVar](TFunction<void(const FString&)> callback) {callback(MyVar);}, InFunction);
    // TODO: You have to track MyLambdaHandle to be able to unregister it when needed
}

https://answers.unrealengine.com/questions/715835/bindufunction-with-variable-capture-in-c.html

示例2:

使用固定参数绑定

DECLARE_DELEGATE(RefreshOne);
     
class MyClass
{
    public:
    MyClass()
    {
        one.BindRaw(this, &MyClass::MyFunction, (uint8)1);
    }

    void MyFunction(uint8 Val)
    {
        // Whatever
    }

    void Invoke()
    {
        one.Execute(); // Will call this->MyFunction((uint8)1);
    }

    private:
        RefreshOne one;
};

使用动态参数绑定

DECLARE_DELEGATE_TwoParams(Delegate, const TCHAR*, float);
 
struct MyClass
{
    MyClass()
    {
        del.BindRaw(this, &MyClass::MyFunction, (uint8)1, 'x');
    }

    void MyFunction(const TCHAR* a, float b, uint8 c, char d)
    {
        // Whatever
    }

    void Invoke()
    {
        del.Execute(TEXT("Hello"), 3.14f); // Will call this->MyFunction(TEXT("Hello"), 3.14f, (uint8)1, 'x');
    }

    Delegate del;
};

Bind delegate with one parameter
https://answers.unrealengine.com/questions/109955/bind-delegate-with-one-parameter.html

Event示例

Define:

UCLASS()
class MYPROJ_API AMyGameMode : public AGameMode
{
    GENERATED_BODY()
    
public:
 
    AHGameMode();
 
    //定义Event
    DECLARE_EVENT(MyUObject, MyInitEvent)

    //定义带参数的Event
    //DECLARE_EVENT_OneParam(MyUObject, MyInitEvent, FMyStruct*)
    
    //用于获取Event引用的函数,方便在GameMode之外执行binding
    MyInitEvent& OnInitialize() { return InitEvent; }
    
private:
    //Event实例化
    MyInitEvent InitEvent;
    
    //Event的binding指针
    FDelegateHandle DHandle;
}

Binding:

MyUObject* MyObj = nullptr;    //这里假设创建MyUObject
if (MyObj)
{
    DHandle = MyGameMode->OnInitialize().AddUObject(MyObj, &MyUObject::TestFun);
}

Boradcast:

void AMyGameMode::BeginPlay()
{
    InitEvent.Broadcast();
}

Unbinding:

MyGameMode->OnInitialize().Remove(DHandle);

Difference

BindRaw、BindUObject、BindUFunction区别
  • BindRaw():针对非UObject类型的class。
  • BindUObject():针对UObject类型Class的非UFUNCTION()函数。
  • BindUFunction:针对UObject类型Class的UFUNCTION()函数。

BindRaw 示例:

FTimerDelegate TimerDel;
TimerDel.BindRaw(this, &MyClass::MyFunction);

BindUObject 示例:

FTimerDelegate TimerDel;
TimerDel.BindUObject(this, &AMyCharacter::MyUFunction);

BindUFunction 示例:

TimerDel.BindUFunction(this, FName("TestFun"));
TScriptDelegate 与 DECLARE_DELEGATE 区别

引擎提供了两种用于绑定函数的代理对象:

  • FScriptDelegate
  • FMulticastScriptDelegate

这两种对象只能使用BindUFunction一种方式绑定,因为他们使用的是DECLARE_DYNAMIC_MULTICAST_DELEGATE,这种代理对象支持转化为stream,可以在网络中传递。
FTimerDelegate 之所以能有三种方式绑定,是因为它是由DECLARE_DELEGATE定义。

Event 和 Delegate 区别
  • Delegate 只能绑定一个回调函数,Delegate执行Execute()函数时,只会触发事先绑定的一个函数;Event可以绑定任意个函数,一旦执行Event的Broadcast()函数,所有回调函数按Add顺序依次执行。
  • Dynamic Multicast Delegate也可以同时绑定多个回调函数,但是其运行效率要比 Event 慢。
  • Event没有返回值,而Delegate可以,例如:DECLARE_DELEGATE_RetVal_OneParam( ReturnValueType, DelegateName, Param1Type )

注意:UE早期版本两者都没有返回值。

Dynamic Delegate 与 常规 Delegate 区别

1, Dynamic Delegates 可以被序列化:即他们的注册函数可以通过名称查找获取,代价是比普通Delegate速度慢。

2, 普通Delegate定义参数时,如果使用引用类型,函数执行时传递的实际参数无效,如果要使用引用类型的参数,则要使用Dynamic Delegates。 例如,以TArray的引用类型为例:

DECLARE_DYNAMIC_DELEGATE_OneParam(FMyDelegate, const TArray<FString>& MyArray);

APIs

AddDynamic

UE4提供了一个宏:AddDynamic,作用是省去编写FScriptDelegate

例如,原本要这么写:

FScriptDelegate DelegateBegin;
DelegateBegin.BindUFunction(PlayerController, "OnWeaponOverlapBegin");
GunMeshComp->OnComponentBeginOverlap.Add(DelegateBegin);

使用AddDynamic可以这么写:

GunMeshComp->OnComponentBeginOverlap.AddDynamic(this, &ATestTDCharacter::OnWeaponOverlapBegin);

使用AddDynamic注册函数时,函数仍然需要添加UFUNCTION()。

AddLambda

If you don’t want to define callback function as member function of class, define callback function in where it be registered, you can use AddLambda.

Example:
Engine\Source\Editor\SequenceRecorder\Private\SequenceRecorderModule.cpp

virtual void StartupModule() override
{
    // register standalone UI
    auto RegisterTabSpawner = []()
    {
        FGlobalTabmanager::Get()->RegisterNomadTabSpawner(SequenceRecorderTabName, FOnSpawnTab::CreateStatic(&FSequenceRecorderModule::SpawnSequenceRecorderTab))
        .SetGroup(WorkspaceMenu::GetMenuStructure().GetLevelEditorCategory())
        .SetDisplayName(LOCTEXT("SequenceRecorderTabTitle", "Sequence Recorder"))
        .SetTooltipText(LOCTEXT("SequenceRecorderTooltipText", "Open the Sequence Recorder tab."))
        .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "SequenceRecorder.TabIcon"));
    };
    
    LevelEditorTabManagerChangedHandle = LevelEditorModule.OnTabManagerChanged().AddLambda(RegisterTabSpawner);
}

天上的神明和星辰,人间的艺术与真纯,我们所敬畏和景仰的,莫过于此。-丰子恺