UE文档的解释代理:
https://docs.unrealengine.com/5.1/en-US/delegates-and-lamba-functions-in-unreal-engine/https://docs.unrealengine.com/5.1/zh-CN/delegates-and-lamba-functions-in-unreal-engine/https://docs.unrealengine.com/5.1/en-US/delegates-and-lamba-functions-in-unreal-engine/
代理发生的情境:
A类有对象指针a指向其实例对象,B类里有对象指针b指向其实例对象,在A里发生了函数aFun,想要在函数aFun发生的时候通知到B中,并且在B类中调用bFun函数,并且期望在这个途中传递一些参数F(b里面能拿到a,但我不期望a再持有一个b的对象这样耦合太高,简单来说就是我期望在A的aFun的时间里在B中拿到A中的一些参数去做某些事)
单播代理的声明委托
如需声明委托,请使用下文所述的宏。请根据与委托相绑定的函数(或多个函数)的函数签名来选择宏。每个宏都为新的委托类型名称、函数返回类型(如果不是 void
函数)及其参数提供了参数。当前,支持以下使用任意组合的委托签名:
-
返回一个值的函数。
-
声明为
常
函数。 -
最多4个"载荷"变量。
-
最多8个函数参数。
使用此表格查找要用于声明委托的生命宏。
函数签名 |
声明宏 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
委托函数支持与UFunctions相同的说明符,但使用 UDELEGATE
宏而不是 UFUNCTION
。例如,以下代码将 BlueprintAuthorityOnly
说明符添加到 FInstigatedAnyDamageSignature
委托中
UDELEGATE(BlueprintAuthorityOnly)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FInstigatedAnyDamageSignature, float, Damage, const UDamageType*, DamageType, AActor*, DamagedActor, AActor*, DamageCauser);
关于组播委托、动态委托和封装委托,上述宏的变体如下:
-
DECLARE_MULTICAST_DELEGATE...
-
DECLARE_DYNAMIC_DELEGATE...
-
DECLARE_DYNAMIC_MULTICAST_DELEGATE...
-
DECLARE_DYNAMIC_DELEGATE...
-
DECLARE_DYNAMIC_MULTICAST_DELEGATE...
委托签名声明可存在于全局范围内、命名空间内、甚至类声明内。此类声明可能不在于函数体内。
请参见动态委托 和委托了解有关声明此类类型委托的更多信息。
委托函数支持与UFunctions相同的说明符,但使用 UDELEGATE
宏而不是 UFUNCTION
。
单播代理的绑定函数(UE文档的解释)
函数 |
描述 |
---|---|
|
绑定到现有委托对象。 |
|
绑定原始C++指针全局函数委托。 |
|
绑定原始C++指针委托。由于原始指针不使用任何类型的引用,因此在删除目标对象后调用`Execute |
|
绑定一个函子。这通常用于拉姆达函数。 |
|
绑定基于指针的共享成员函数委托。共享指针委托会保留对对象的弱引用。可使用 |
|
绑定 |
|
取消绑定此委托。 |
单播代理的执行函数(UE文档的解释)
执行函数 |
描述 |
---|---|
|
不检查其绑定情况即执行一个委托 |
|
检查一个委托是否已绑定,如是,则调用Execute |
|
检查一个委托是否已绑定,经常出现在包含 |
单播无参无返回值
#define DECLARE_DELEGATE( DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, void )
DECLARE_DELEGATE(DelegateName);
单播 含一个参数代理
#define DECLARE_DELEGATE_OneParam( DelegateName, Param1Type ) FUNC_DECLARE_DELEGATE( DelegateName, void, Param1Type )
1. 在A的全局域中声明这个代理,使得在哪都可以用这个代理
DECLARE_DELEGATE_OneParam(GMDelegateOne, FString);
2.在class A中声明一个代理类型的对象
GMDelegateOne GmDel;
3.在A的aFun里进行代理调用,aFun()中
//FString ACallTest(TEXT("叮铃铃叮铃铃"));
使用这种调用方式
if (GmDel.IsBound())
{
GmDel.Execute(ACallTest); //使用execute前需要检查IsBound()
}
或者下面的
GmDel.ExecuteIfBound(ACallTest); //调用代理通知,安全,但是有返回类型的回调函数无法使用此函数执行回调
4.在class B中声明一个回调函数
public:
UFUNCTION()
void DelegateCallBackOnB(FString string); //随便取名字
5.在B的一个地方进行代理绑定(必须要在在A的aFun调用之前)(一般是在OnStart,BeginPlay,也可视情况变动)
b->GmDel.BindUObject(this, &UB::DelegateCallBackOnB); //this为UB类,UB::Say为想要A通知后B中执行的函数
6.定义B类中的回调事件
void UB::DelegateCallBackOnB(FString string)
{
UE_LOG(LogTemp, Warning, TEXT("A Call Text is %s"), string); //叮铃铃叮铃铃
}
单播含一个参数含返回值:
#define DECLARE_DELEGATE_RetVal_OneParam( ReturnValueType, DelegateName, Param1Type ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType, Param1Type )
返回值为DECLARE_DELEGATE_RetVal_OneParam后面的第一个参数,然后是代理类型的名称,然后是参数类型
单播代理常用绑定函数
BindUObject 绑定UObject类型对象成员函数的代理
BindSP 绑定基于共享引用的成员函数代理
BindRaw 绑定原始自定义对象成员函数的代理,操作调用需要注意执行需要检查IsBound
BindStatic 绑定全局函数成为代理
UnBind 解除绑定代理关系
绑定需要注意,绑定中传递的对象类型必须和函数指针所属类的类型相同否则绑定会报错
GmDel.BindUObject(act,&AMyActor::Say);
单播代理调用执行
为了保证调用的安全性,执行Execute函数之前需要检查是否存在有效绑定使用函数IsBound
Execute 调用代理通知,不安全,需要注意
ExecuteIfBound 调用代理通知,安全,但是有返回类型的回调函数无法使用此函数执行回调
IsBound 检查当前是否存在有效代理绑定
GmDel.ExecuteIfBound();
单播代理只能绑定一个通知对象,无法进行多个对象通知,但可以有返回值
多播代理
多播代理的绑定函数(UE文档的解释)
多播委托可以绑定多个函数,当委托触发时,将调用所有这些函数。因此,绑定函数在语义上与数组更加类似。
函数 |
说明 |
---|---|
"Add()" |
将函数委托添加到该多播委托的调用列表中。 |
"AddStatic()" |
添加原始C++指针全局函数委托。 |
"AddRaw()" |
添加原始C++指针委托。原始指针不使用任何类型的引用,因此如果从委托下面删除了对象,则调用此函数可能不安全。调用Execute()时请小心! |
"AddSP()" |
添加基于共享指针的(快速、非线程安全)成员函数委托。共享指针委托保留对对象的弱引用。 |
"AddUObject()" |
添加基于UObject的成员函数委托。UObject委托保留对对象的弱引用。 |
"Remove()" |
从该多播委托的调用列表中删除函数(性能为O(N))。请注意,委托的顺序可能不会被保留! |
"RemoveAll()" |
从该多播委托的调用列表中删除绑定到指定UserObject的所有函数。请注意,委托的顺序可能不会被保留! |
多播代理的执行函数(UE文档的解释)
多播委托允许您附加多个函数委托,然后通过调用多播委托的"Broadcast()"函数一次性同时执行它们。多播委托签名不得使用返回值。
在多播委托上调用"Broadcast()"总是安全的,即使是在没有任何绑定时也是如此。唯一需要注意的是,如果您使用委托来初始化输出变量,通常会带来非常不利的后果。
调用"Broadcast()"时绑定函数的执行顺序尚未定义。执行顺序可能与函数的添加顺序不相同。
函数 |
说明 |
---|---|
"Broadcast()" |
将该委托广播给所有绑定的对象,但可能已过期的对象除外。 |
多播无参
无法构建具有返回值的多播代理
#define DECLARE_MULTICAST_DELEGATE( DelegateName ) FUNC_DECLARE_MULTICAST_DELEGATE( DelegateName, void )
多播带一个参数的代理
#define DECLARE_MULTICAST_DELEGATE_OneParam( DelegateName, Param1Type ) FUNC_DECLARE_MULTICAST_DELEGATE( DelegateName, void, Param1Type )
1. 在A的全局域中声明这个代理,使得在哪都可以用这个代理
DECLARE_MULTICAST_DELEGATE_OneParam(GMDelegateOne, FString);
2.在class A中声明一个代理类型的对象
GMDelegateOne GmDel;
3.在A的aFun里进行代理调用,aFun()中
//FString ACallTest(TEXT("叮铃铃叮铃铃"));
GmDel.Broadcast(ACallTest);
5.在B的一个地方进行代理绑定(必须要在在A的aFun调用之前)(一般是在OnStart,BeginPlay,也可视情况变动)
b->GmDel.AddUObject(this, &UB::DelegateCallBackOnB); //this为UB类,UB::Say为想要A通知后B中执行的函数
6.定义B类中的回调事件
void UB::DelegateCallBackOnB(FString string)
{
UE_LOG(LogTemp, Warning, TEXT("A Call Text is %s"), string); //叮铃铃叮铃铃
}
绑定函数
|
添加函数代理到此多播代理的调用列表。 |
|
添加一个原始的C++指针全局函数代理。 |
|
添加一个原始的C++指针代理。原始指针不使用任何引用,所以如果从代理的底层删除了该对象,那么调用它可能是不安全的。因此,当调用Execute()时一定要小心! |
|
添加一个基于共享指针(快速,非线程安全)的成员函数代理。共享指针代理保持到您的对象的弱引用。 |
|
添加一个基于UObject的成员函数代理。UObject 代理保持到您的对象的弱引用。 |
|
将函数从这个多播代理的调用列表中移除(性能为O(N))。请注意代理的顺序可能不会被保留! |
|
将所有函数从与特定UserObject绑定的多播代理的调用列表中移除。请注意代理的顺序可能不会被保留! |
广播
调用函数Broadcast,但是调用不保证执行顺序的正确性
构建步骤
1.使用宏构建代理类型
2.使用代理类型构建多播代理对象
3.添加绑定代理
4.执行调用
动态代理
声明动态委托(UE文档的解释)
动态委托的声明方式与声明标准委托相同, 只是前者使用动态委托专属的宏变体。
声明宏 |
描述 |
---|---|
|
创建一个动态委托。 |
|
创建一个动态组播委托。 |
动态委托绑定(UE文档的解释)
辅助宏 |
说明 |
---|---|
|
用于在动态委托上调用BindDynamic()的辅助宏。自动生成函数命名字符串。 |
|
用于在动态组播委托上调用AddDynamic()的辅助宏。自动生成函数命名字符串。 |
|
用于在动态组播委托上调用RemoveDynamic()的辅助宏。自动生成函数命名字符串。 |
执行动态委托(UE文档的解释)
通过调用委托的 Execute()
函数执行绑定到委托的函数。执行前须检查委托是否已绑定。 此操作是为了使代码更安全,因为有时委托可能含有未初始化且被后续访问的返回值和输出参数。 执行未绑定的委托在某些情况下确实可能导致内存混乱。可调用 IsBound()
检查是否可安全执行委托。 同时,对于无返回值的委托,可调用 ExecuteIfBound()
,但需注意输出参数可能未初始化。
执行函数 |
描述 |
---|---|
|
不检查其绑定情况即执行一个委托 |
|
检查一个委托是否已绑定,如是,则调用Execute |
|
检查一个委托是否已绑定,经常出现在包含 |
动态代理介绍
允许被序列化的数据结构,这将使得代理可以被数据化提供给蓝图进行使用,达到在CPP中调用代理广播,将事件通知到蓝图中
动态代理和普通代理基本相同,分为单向和多向,动态代理无法使用带有返回值的函数进行构建(动态单播除外,并且单播无法再蓝图中绑定无法使用宏BlueprintAssignable修饰)
UE中的大部分通知时间均使用动态代理(方便蓝图操作),如碰撞通知
构建一个动态代理
DECLARE_DYNAMIC_DELEGATE[...Const,..._RetVal,etc.](DelegateName)
创建一个动态的多播代理
DECLARE_DYNAMIC_MULTICAST_DELEGATE[...Const,...RetVal,etc.](DelegateName)
操作函数
BindDynamic(UserObject,FuncName) 在动态代理上调用BindDynamic()的辅助宏
AddDynamic(UserObject,FuncName) 在动态多播代理上调用AddDynamic()的辅助宏
RemoveDynamic(UserObject,FuncName) 在动态多播代理上调用RemoveDynamic()的辅助宏
动态代理与单播代理和多播代理的区别
1.动态代理构建类型名称需要用F开头(动态代理实现机制构建了类)
2.动态代理对象类型可以使用UPROPERTY标记,其他代理均无法使用(不加编译可过,调用会出错)
3.动态代理绑定对象的函数需要使用UFUNCTION进行描述(因为需要跟随代理被序列化)
构建
动态单播构建
DECLARE_DYNAMIC_DELEGATE(FGMDynDelegate); //分号不要漏了
FGMDynDelegate GmDyDele;
GmDyDele.BindDynamic(ActorName,&AMyActor::Say);
GmDyDele.ExcuteIfBound();
动态多播构建操作同上
动态代理用于蓝图
需要注意的是,在构建动态代理提供蓝图使用时,需要在代理上增加标记宏UPROPERTY(BlueprintAssignable)
构建宏
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGMDynMulDele);
对象声明
UPROPERTY(BlueprintAssignable,BlueprintReadWrite)
FGMDynMulDele OnGmDynMulDele;
在C++中调用
if(OnGmDynMulDele.IsBound())
{
OnGmDynMulDele.Broadcast();
}