flushia系统_全面理解UE4委托 - osc_iazh5wbp的个人空间 - OSCHINA - 中文开源技术交流社区...

本文深入探讨了UE4中的委托(delegate)概念,包括单播、多播、动态和事件代理的使用。委托作为安全的函数指针,用于解耦对象间的关联。通过DECLARE_*宏定义自定义委托类型,绑定不同类型的函数,如静态函数、成员函数、Lambda表达式等,并通过ExecuteIfBound或Execute触发。此外,文章还介绍了动态和多播代理,以及如何通过Broadcast广播触发。委托的应用场景广泛,如游戏对象交互、事件响应等。
摘要由CSDN通过智能技术生成

UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系

监听者通过将响应函数绑定到委托上,使得委托触发时立即收到通知,并进行相关逻辑处理

委托,又称代理,本质是一个特殊类的对象,它内部可以储存(一个或多个)函数指针、调用参数和返回值

委托的作用如同函数指针,但它更安全(支持编译期类型检查),而且更易于使用

UE4在DelegateCombinations.h提供了一些宏来方便开发者快速来自定义一个delegate类型

大致地使用流程如下:

① 使用DECLARE_*宏声明一个自定义delegate类型FDelegateXXX

② 声明一个FDelegateXXX类型的代理对象

③ 绑定需要执行的函数指针到代理对象上

④ 触发代理对象中的函数指针会立即执行

⑤ 不需要某个函数指针时,可将其从代理对象中解绑

UE4提供了五类Delegate:

名称

是否支持反射

以及序列化

能否有返回值

是否支持

Payload参数

触发代理

绑定个数

支持的绑定实例类型

单播代理

DECLARE_DELEGATE_*

DECLARE_DELEGATE_RetVal_*

可以有

① 无返回值情况

bool ExecuteIfBound(...)

void Execute(...)

② 有返回值情况

RetValue Execute(...)

注:RetValue为绑定函数

的返回值

1个

① Static函数  // BindStatic

② 普通C++对象的成员函数  // BindRaw

③ Lambda表达式  // BindLambda

// BindWeakLambda

④ 与某个UObject对象关联的Lambda表达式

⑤ 共享引用包裹的普通C++对象的成员函数  // BindSP

// BindThreadSafeSP

⑥ 线程安全的共享引用包裹的普通C++对象的成员函数

⑦ UObject对象的成员函数 // BindUObject

⑧ UObject对象的UFunction成员函数  // BindUFunction

多播代理

(组播代理)

DECLARE_MULTICAST_DELEGATE_*

void Broadcast(...)

多个

① Static函数  // AddStatic

② 普通C++对象的成员函数  // AddRaw

③ Lambda表达式  // AddLambda

// AddWeakLambda

④ 与某个UObject对象关联的Lambda表达式

⑤ 共享引用包裹的普通C++对象的成员函数  // AddSP

// AddThreadSafeSP

⑥ 线程安全的共享引用包裹的普通C++对象的成员函数

⑦ UObject对象的成员函数 // AddUObject

⑧ UObject对象的UFunction成员函数 // AddUFunction

--------------------------------------

另外,可以创建FDelegate对象,然后调用Add函数

事件

DECLARE_EVENT_*

void Broadcast(...)

多个

同上

动态代理

DECLARE_DYNAMIC_DELEGATE_*

DECLARE_DYNAMIC_DELEGATE_RetVal_*

可以有

① 无返回值情况

bool ExecuteIfBound(...)

void Execute(...)

② 有返回值情况

RetValue Execute(...)

注:RetValue为绑定函数

的返回值

1个

// BindUFunction

UObject对象的UFunction成员函数

--------------------------------------

另外,可以使用BindDynamic宏来绑定

动态多播代理

DECLARE_DYNAMIC_MULTICAST_DELEGATE_*

蓝图中可使用

void Broadcast(...)

多个

// Add函数、AddDynamic宏

// AddUnique函数、AddUniqueDynamic宏

UObject对象的UFunction成员函数

注1:Playload为代理绑定时传递的额外参数变量列表,这些参数会存储在代理对象内部;在触发代理时,Playload会紧跟着Execute、ExecuteInBound或Broadcast传入的参数之后,填充到绑定函数指针的参数列表中,然后执行

DECLARE_DELEGATE_OneParam(FTestDelegate, int);static void StaticDelegateProc(intnCode)

{

UE_LOG(LogTemp, Log, TEXT("StaticDelegateProc : %d"), nCode);

}static void StaticDelegateProcTest(int nCode, floatdis, FString txt)

{

UE_LOG(LogTemp, Log, TEXT("StaticDelegateProcTest : %d %f %s"), nCode, dis, *txt);

}

FTestDelegate DelegateObj1;

DelegateObj1.BindStatic(StaticDelegateProc);

DelegateObj1.ExecuteIfBound(1);//Playload示例FTestDelegate DelegateObj2;

DelegateObj2.BindStatic(StaticDelegateProcTest,12.0f, FString(TEXT("Hello"))); //12.0f、FString(TEXT("Hello"))为绑定时传入的Playload参数

DelegateObj2.ExecuteIfBound(2);

注2:动态代理、动态多播代理不支持Playload,因此,绑定函数与代理对象的参数、返回值必须完全一致

下面为一些类型示例:

① 单播代理

DECLARE_DELEGATE( FSimpleDelegate );  // 无参、无返回值

DECLARE_DELEGATE_OneParam(FPakEncryptionKeyDelegate, uint8[32]);  // 1个参数、无返回值

DECLARE_DELEGATE_TwoParams(FPakSigningKeysDelegate, TArray&, TArray&);  // 2个参数、无返回值

DECLARE_DELEGATE_RetVal_ThreeParams(bool, FOnMountPak, const FString&, int32, IPlatformFile::FDirectoryVisitor*);  // 3个参数、bool返回值

② 多播代理

DECLARE_MULTICAST_DELEGATE( FSimpleMulticastDelegate );  // 无参

DECLARE_MULTICAST_DELEGATE_TwoParams(FOnTreeStarted, const UBehaviorTreeComponent&, const UBehaviorTree& );  // 2个参数

DECLARE_MULTICAST_DELEGATE_FourParams(FOnOpenURL, UIApplication*, NSURL*, NSString*, id); // 4个参数

③ 事件

DECLARE_EVENT(UWorld, FOnTickFlushEvent);  // 无参

DECLARE_EVENT_OneParam(IWebBrowserWindow, FOnTitleChanged, FString); // 1个参数

④ 动态代理

DECLARE_DYNAMIC_DELEGATE(FOnGameWindowCloseButtonClickedDelegate);  // 无参、无返回值

DECLARE_DYNAMIC_DELEGATE_OneParam(FOnAssetLoaded, class UObject*, Loaded); // 1个参数、无返回值

DECLARE_DYNAMIC_DELEGATE_RetVal(EMouseCursor::Type, FGetMouseCursor); // 无参、EMouseCursor::Type返回值

DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(UWidget*, FGenerateWidgetForObject, UObject*, Item); // 1个参数、UWidget*返回值

DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(FEventReply, FOnPointerEvent, FGeometry, MyGeometry, const FPointerEvent&, MouseEvent);  // 2个参数、FEventReply返回值

⑤ 动态多播代理

DECLARE_DYNAMIC_MULTICAST_DELEGATE( FLevelStreamingLoadedStatus );  // 无参

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLandedSignature, const FHitResult&, Hit);  // 1个参数

DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FMovementModeChangedSignature, class ACharacter*, Character, EMovementMode, PrevMovementMode, uint8, PreviousCustomMode);  // 3个参数

单播代理

1. 定义代理类型

① 定义返回值为void含1个int类型参数的单播代理类型FCharacterDelegate

DECLARE_DELEGATE_OneParam(FCharacterDelegate, int); // TBaseDelegate

47920dddfa1113f96aa2b78d3d85296a.png

② 定义返回值为bool含1个int类型参数的单播代理类型FCharacterDelegate_RetVal

DECLARE_DELEGATE_RetVal_OneParam(bool, FCharacterDelegate_RetVal, int);  // TBaseDelegate

133bcdbdb9449c75a8ee1053b9b7ea34.png

2.  声明代理对象

FCharacterDelegate CharacterDelegate;

FCharacterDelegate_RetVal CharacterDelegateRetVal;

以FCharacterDelegate为例,讲述单播代理的绑定、触发执行及存在的风险

/*** TPSProjectCharacter.h ***/UCLASS()class UDelegatepTestClass : publicUObject

{

GENERATED_BODY()protected:int m_nValue = 0;public:void DelegateProc1(intnCode)

{this->m_nValue =nCode;

UE_LOG(LogTemp, Log, TEXT("DelegateProc1 : %d"), nCode);

}

UFUNCTION()void DelegateUFunctionProc1(intnCode)

{this->m_nValue =nCode;

UE_LOG(LogTemp, Log, TEXT("DelegateUFunctionProc1 : %d"), nCode);

}

};classDelegateCppTestClass

{int m_nValue = 0;public:void CppDelegateProc(intnCode)

{this->m_nValue =nCode;

UE_LOG(LogTemp, Log, TEXT("CppDelegateProc : %d"), nCode);

}void CppDelegateProc2(intnCode)

{this->m_nValue =nCode;

UE_LOG(LogTemp, Log, TEXT("CppDelegateProc2 : %d"), nCode);

}void CppDelegateProc3(intnCode)

{this->m_nValue =nCode;

UE_LOG(LogTemp, Log, TEXT("CppDelegateProc3 : %d"), nCode);

}void CppDelegateProc4(intnCode)

{this->m_nValue =nCode;

UE_LOG(LogTemp, Log, TEXT("CppDelegateProc4 : %d"), nCode);

}void CppDelegateProc5(intnCode)

{this->m_nValue =nCode;

UE_LOG(LogTemp, Log, TEXT("CppDelegateProc5 : %d"), nCode);

}

};

UCLASS(config=Game)class ATPSProjectCharacter : publicACharacter

{

GENERATED_BODY()public:static void StaticCharacterDelegateProc(intnCode);voidOnBind();voidOnExecute();voidOnUnbind();

FCharacterDelegate CharacterDelegate1;

FCharacterDelegate CharacterDelegate2;

FCharacterDelegate CharacterDelegate3;

FCharacterDelegate CharacterDelegate4;

FCharacterDelegate CharacterDelegate5;

FCharacterDelegate CharacterDelegate6;

FCharacterDelegate CharacterDelegate7;

FCharacterDelegate CharacterDelegate8;

FCharacterDelegate CharacterDelegate9;

FCharacterDelegate CharacterDelegate10;

FCharacterDelegate CharacterDelegate11;

FCharacterDelegate CharacterDelegate12;

FCharacterDelegate CharacterDelegate13;

};/*** TPSProjectCharacter.cpp ***/

static void StaticDelegateProc(intnCode)

{

UE_LOG(LogTemp, Log, TEXT("StaticDelegateProc : %d"), nCode);

}void ATPSProjectCharacter::StaticCharacterDelegateProc(intnCode)

{

UE_LOG(LogTemp, Log, TEXT("StaticCharacterDelegateProc : %d"), nCode);

}voidATPSProjectCharacter::OnBind()

{//Bind Static

CharacterDelegate1.BindStatic(StaticDelegateProc);

CharacterDelegate2.BindStatic(ATPSProjectCharacter::StaticCharacterDelegateProc);//Bind Raw

DelegateCppTestClass Obj1;

CharacterDelegate3.BindRaw(&Obj1, &DelegateCppTestClass::CppDelegateProc);//Bind Lambda

auto LambdaDelegateProc = [](int nCode)->void{

UE_LOG(LogTemp, Log, TEXT("LambdaDelegateProc : %d"), nCode);

};

CharacterDelegate4.BindLambda(LambdaDelegateProc);

CharacterDelegate5.BindLambda(

[](int nCode)->void{

UE_LOG(LogTemp, Log, TEXT("LambdaDelegateProc2 : %d"), nCode);

}

);//Bind Weak Lambda

auto WeakLambdaDelegateProc = [](int nCode)->void{

UE_LOG(LogTemp, Log, TEXT("WeakLambdaDelegateProc : %d"), nCode);

};

UDelegatepTestClass* UObj1 = NewObject(this, UDelegatepTestClass::StaticClass());

CharacterDelegate6.BindWeakLambda(UObj1, WeakLambdaDelegateProc);

UDelegatepTestClass* UObj2 = NewObject(this, UDelegatepTestClass::StaticClass());

CharacterDelegate7.BindWeakLambda(

UObj2, [](int nCode)->void{

UE_LOG(LogTemp, Log, TEXT("WeakLambdaDelegateProc2 : %d"), nCode);

}

);//Bind SP(Shared Pointer)

TSharedRef ObjSP1 = MakeShareable(newDelegateCppTestClass());

CharacterDelegate8.BindSP(ObjSP1,&DelegateCppTestClass::CppDelegateProc2);

TSharedRef ObjSP2 = MakeShared();

CharacterDelegate9.BindSP(ObjSP2,&DelegateCppTestClass::CppDelegateProc3);//Bind Thread Safe SP(Shared Pointer)

TSharedRef ObjSafeSP1 = MakeShareable(newDelegateCppTestClass());

CharacterDelegate10.BindThreadSafeSP(ObjSafeSP1,&DelegateCppTestClass::CppDelegateProc4);

TSharedRef ObjSafeSP2 = MakeShared();

CharacterDelegate11.BindThreadSafeSP(ObjSafeSP2,&DelegateCppTestClass::CppDelegateProc5);//Bind UObject

UDelegatepTestClass* UObj3 = NewObject(this, UDelegatepTestClass::StaticClass());

CharacterDelegate12.BindUObject(UObj3,&UDelegatepTestClass::DelegateProc1);//Bind UFunction

UDelegatepTestClass* UObj4 = NewObject(this, UDelegatepTestClass::StaticClass());

CharacterDelegate13.BindUFunction(UObj4, STATIC_FUNCTION_FNAME(TEXT("UDelegatepTestClass::DelegateUFunctionProc1")));

}voidATPSProjectCharacter::OnExecute()

{

CharacterDelegate1.ExecuteIfBound(1);

CharacterDelegate2.ExecuteIfBound(2);

CharacterDelegate3.ExecuteIfBound(3);

CharacterDelegate4.ExecuteIfBound(4);

CharacterDelegate5.ExecuteIfBound(5);

CharacterDelegate6.ExecuteIfBound(6);

CharacterDelegate7.ExecuteIfBound(7);

CharacterDelegate8.ExecuteIfBound(8);

CharacterDelegate9.ExecuteIfBound(9);

CharacterDelegate10.ExecuteIfBound(10);

CharacterDelegate11.ExecuteIfBound(11);

CharacterDelegate12.ExecuteIfBound(12);if(CharacterDelegate13.IsBound())

{

CharacterDelegate13.Execute(13);

}

}voidATPSProjectCharacter::OnUnbind()

{

CharacterDelegate1.Unbind();

CharacterDelegate2.Unbind();

CharacterDelegate3.Unbind();

CharacterDelegate4.Unbind();

CharacterDelegate5.Unbind();

CharacterDelegate6.Unbind();

CharacterDelegate7.Unbind();

CharacterDelegate8.Unbind();

CharacterDelegate9.Unbind();

CharacterDelegate10.Unbind();

CharacterDelegate11.Unbind();

CharacterDelegate12.Unbind();

CharacterDelegate13.Unbind();

}

注1:BindRaw函数用于绑定普通c++对象的成员函数,若该c++对象已被销毁,触发代理执行该对象的成员函数,将会导致内存违规操作

注2:BindLambda函数用于绑定lambda表达式,若lambda表达式捕获外部变量已被销毁,触发代理执行lambda表达式,将会导致内存违规操作

注3:BindWeakLambda、BindUObject、BindUFunction绑定时会与一个UObject对象进行弱引用关联(不影响该对象被gc回收)

若UObject对象被gc回收,直接调用Execute触发代理,将会导致内存违规操作;可先调用IsBound或调用ExecuteIfBound触发代理来检查该UObject的有效性,再来执行代理

注4:BindSP、BindThreadSafeSP绑定时会与一个智能指针对象进行弱引用关联(不影响该对象的内存回收)

若智能指针对象的内存被回收,直接调用Execute触发代理,将会导致内存违规操作;可先调用IsBound或调用ExecuteIfBound触发代理来检查该智能指针对象的有效性,再来执行代理

注5:单播代理对象在被销毁时,会在其析构函数中调用Unbind进行解绑操作

注6:不同的绑定实例类型定义在:DelegateInstancesImpl.h

BindStatic(绑定实例类型为:TBaseStaticDelegateInstance)

849a846ef19efcdb88410eba2e7c1652.png

03747f71255fdb3ce2d9c7605cdeb1ba.png

BindRaw(绑定实例类型为:TBaseRawMethodDelegateInstance)

f9b62a416458ec0020f7da0a53c5aa5e.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值