【UE4 C++】<6>Delegate 委托
2021/4/23 20:25:24
本文主要是介绍【UE4 C++】<6>Delegate 委托,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
概念
定义
- UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系。
监听者通过将响应函数绑定到委托上,使得委托触发时立即收到通知,并进行相关逻辑处理。 - 委托,又称代理,本质是一个特殊类的对象,它内部可以储存(一个或多个)函数指针、调用参数和返回值。
蓝图示例
声明委托
- 委托签名声明可存在于全局范围内、命名空间内、甚至类声明内。此类声明可能不在于函数体内
- 可以是返回一个值的函数。
- 最多4个"载荷"变量。
- 最多8个函数参数。
定义一个无参普通单播委托 DECLARE_DELEGATE( DelegateName ) 定义一个无参普通单播委托, 带返回参数 DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) 定义一个无参动态单播委托, 带返回参数 DECLARE_DYNAMIC_DELEGATE_RetVal 定义一个无参普通多播委托 DECLARE_MULTICAST_DELEGATE( DelegateName ) 定义一个无参事件(特殊的多播委托) DECLARE_EVENT( OwningType, EventName ) 定义一个无参动态单播委托 DECLARE_DYNAMIC_DELEGATE( DelegateName) 定义一个无参动态多播委托 DECLARE_DYNAMIC_MULTICAST_DELEGATE( DelegateName )
单播委托
- 绑定单个可调用对象
- 支持返回值.
- 支持参数
- 不支持反射以及序列化
声明宏
不支持返回,支持参数 DECLARE_DELEGATE_* DECLARE_DELEGATE(DelegateName) DECLARE_DELEGATE_OneParam(DelegateName, Param1Type) DECLARE_DELEGATE_<Num>Params(DelegateName, Param1Type, Param2Type, ...) 支持返回RetValType,支持参数 DECLARE_DELEGATE_RetVal_* DECLARE_DELEGATE_RetVal(RetValType, DelegateName) DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type) DECLARE_DELEGATE_RetVal_<Num>Params(RetValType, DelegateName, Param1Type, Param2Type, ...)
绑定委托
-
Bind
绑定到现有委托对象。 -
BindStatic
绑定原始C++指针全局函数委托。 -
BindRaw
绑定原始C++指针委托。由于原始指针不使用任何类型的引用,因此在删除目标对象后调用Execute 或 ExecuteIfBound
会不安全。 -
BindLambda
-
BindSP
绑定基于指针的共享成员函数委托。共享指针委托会保留对对象的弱引用。可使用 ExecuteIfBound() 进行调用。TSharedRef<FLogWriter> LogWriter(new FLogWriter()); WriteToLogDelegate.BindSP(LogWriter, &FLogWriter::WriteToLog);
-
BindUObject
绑定 UObject 的成员函数委托。UObject 委托会保留对你的对象 UObject 的弱引用。可使用 ExecuteIfBound() 进行调用。 -
BindUFunction
绑定由UFUNCTION标记的函数 -
UnBind
取消绑定此委托。
执行函数
-
Execute
不检查其绑定情况即执行一个委托 -
ExecuteIfBound
检查一个委托是否已绑定,如是,则调用Execute -
IsBound
检查一个委托是否已绑定,经常出现在包含 Execute 调用的代码前
用法示例
不带参数Delegate
//InventoryGameMode.h类外声明 DECLARE_DELEGATE(FStandardDelegateSignature) //InventoryGameMode.h类成员声明变量 FStandardDelegateSignature MyStandardDelegate; //DelegateListener.cpp绑定委托 MyInventoryGM->MyStandardDelegate.BindUObject(this, &ADelegateListener::EnableLight); //DelegateListener.cpp解绑委托 MyInventoryGM->MyStandardDelegate.Unbind(); //MyTriggerVolume.cpp调用委托,间接调用函数 MyInventoryGM->MyStandardDelegate.ExecuteIfBound();
带参数Delegate
//InventoryGameMode.h类外声明 DECLARE_DELEGATE_OneParam(FParamDelegateSignature,FLinearColor) //InventoryGameMode.h类成员声明变量 FParamDelegateSignature MyParamDelegate; //ParamDelegateListener.cpp 绑定委托 MyInventoryGM->MyParamDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor); //ParamDelegateListener.cpp 解绑委托 MyInventoryGM->MyParamDelegate.Unbind(); //MyTriggerVolume.cpp调用委托,间接调用函数 auto Color = FLinearColor(1, 0, 0, 1); MyInventoryGM->MyParamDelegate.ExecuteIfBound(Color);
传递有效负载数据
多播委托
-
可以绑定多个回调函数,当其触发时,所有绑定的回调函数都会执行, 实质是维持了一个单播委托的数组
-
没有返回值.
-
支持参数
-
不支持反射以及序列化
声明宏
DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature) DECLARE_MULTICAST_DELEGATE_OneParam(FMulticastDelegateSignature, FString)
绑定多播委托
-
Add
将函数委托添加到该多播委托的调用列表中。 -
AddStatic
添加原始C++指针全局函数委托。 -
AddRaw
添加原始C++指针委托。原始指针不使用任何类型的引用,因此如果从委托下面删除了对象,则调用此函数可能不安全。调用Execute()时请小心! -
AddSP
添加基于共享指针的(快速、非线程安全)成员函数委托。共享指针委托保留对对象的弱引用。 -
AddUObject
添加基于UObject的成员函数委托。UObject委托保留对对象的弱引用。 -
Remove
从该多播委托的调用列表中删除函数(性能为O(N))。请注意,委托的顺序可能不会被保留! -
RemoveAll
从该多播委托的调用列表中删除绑定到指定UserObject的所有函数。请注意,委托的顺序可能不会被保留!
多播执行
Broadcast
将该委托广播给所有绑定的对象,但可能已过期的对象除外。
用法示例
//InventoryGameMode.h类外声明 DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature) //InventoryGameMode.h类成员声明变量 FMulticastDelegateSignature MyMulticastDelegate; //MulticastDelegateListener.h声明 FDelegateHandle MyDelegateHandle; //MulticastDelegateListener.cpp 绑定委托 MyDelegateHandle = MyInventoryGM->MyMulticastDelegate.AddUObject(this, &AMulticastDelegateListener::ToggleLight); //MulticastDelegateListener.cpp 解绑委托 MyInventoryGM->MyMulticastDelegate.Remove(MyDelegateHandle); //MyTriggerVolume.cpp调用委托,间接调用函数 MyInventoryGM->MyMulticastDelegate.Broadcast(); //绑定lambda FDelegateHandle Handle; Handle=MDOneParam.AddLambda( [](FString str) { UE_LOG(LogTemp,Warning,TEXT("Lambda Call,Param Value:%s"),*str); } )
动态委托
- 支持反射以及序列化,但其执行速度比常规委托慢。可
- 支持返回值
- 不支持参数
- 动态多播委托可以暴露给蓝图,在蓝图中动态绑定相关的函数,而普通的委托和动态单播委托则不行
声明宏
- 参数构成:(委托名,参数类型1,参数名1,参数类型2,参数名2)
动态多播代理的名称开头须为F
,否则会编译或调用报错
DECLARE_DYNAMIC_DELEGATE_* DECLARE_DYNAMIC_DELEGATE(FOnGameWindowCloseButtonClickedDelegate); // 无参、无返回值 DECLARE_DYNAMIC_DELEGATE_OneParam(FOnAssetLoaded, class UObject*, Loaded); // 1个参数、无返回值 DECLARE_DYNAMIC_DELEGATE_RetVal_* 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(FMyDelegate) DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyDelegate, FString, InPrar) DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FNotifyPawnChange, float, PawnHpPercent,float,PawnPhysicalShieldPercent,float, PawnMageShieldPercent);
动态委托绑定
-
BindDynamic( UserObject, FuncName )
-
AddDynamic( UserObject, FuncName )
-
RemoveDynamic( UserObject, FuncName )
解绑单个 -
Clear
全部解绑
动态多播也支持使用Remove和Removeall,用法与多播一样
执行动态委托
-
Execute
不检查其绑定情况即执行一个动态委托 -
ExecuteIfBound
检查一个动态委托是否已绑定,如是,则调用Execute -
IsBound
检查一个动态委托是否已绑定,经常出现在包含 Execute 调用的代码前 -
Broadcast
将该动态多播
委托广播给所有绑定的对象,但可能已过期的对象除外。
普通委托可以作为函数参数使用
//动态委托 DECLARE_DYNAMIC_DELEGATE(FWDE_Dy_Sl_Zero); //委托变量作为参数 UFUNCTION(BlueprintCallable, Category = "FrameWork") void RegFunDel(FWDE_Dy_Sl_Zero TargetFun);
动态多播委托
-
AddDynamic绑定的方法得被
UFUNCTION
标记,否则绑定无效 -
动态代理对象类型可以使用
UPROPERTY
标记,并设置为BlueprintAssignable
,从而暴露给蓝图使用,其他代理均无法使用(不加编译可过,调用出错)
ECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FNotifyPawnChange, float, PawnHpPercent,float,PawnPhysicalShieldPercent,float, PawnMageShieldPercent); UPROPERTY(BlueprintAssignable) FNotifyPawnChange NotifyPawnChange; NotifyPawnChange.Broadcast(PawnHpPercent, PawnPhysicalShieldPercent, PawnMageShieldPercent);
事件
事件与 组播委托 十分相似。虽然任意类均可绑定事件,但只有声明事件的类可以调用事件 的 Broadcast、IsBound 和 Clear 函数。这意味着事件对象可在公共接口中公开,而无需让外部类访问这些敏感度函数。事件使用情况有:在纯抽象类中包含回调、限制外部类调用 Broadcast、IsBound 和 Clear 函数。
声明宏
DECLARE_EVENT( OwningType, EventName ) 创建一个事件。 DECLARE_EVENT_OneParam( OwningType, EventName, Param1Type ) 创建带一个参数的事件。 DECLARE_EVENT_TwoParams( OwningType, EventName, Param1Type, Param2Type ) 创建带两个参数的事件。 DECLARE_EVENT_<Num>Params( OwningType, EventName, Param1Type, Param2Type, ...) 创建带 N 个参数的事件。
绑定事件
与多播委托方式相同
事件执行
事件允许附带多个函数委托,然后调用事件的 Broadcast() 函数将它们一次性全部执行。
Broadcast()
将此事件广播到所有绑定对象,已失效的对象除外。
用法示例
普通用法
//MyTriggerVolume.h 类外声明 DECLARE_EVENT(AMyTriggerVolume,FPlayerEntered) //MyTriggerVolume.h 类内声明 FPlayerEntered OnPlayerEntered; //MyTriggerVolume.cpp 调用委托 OnPlayerEntered.Broadcast(); //TriggerVolEventListener.cpp 绑定委托 TriggerEventSource->OnPlayerEntered.AddUObject(this, &ATriggerVolEventListener::OnTriggerEvent);
通常用法
将Event定义于类内,通常将Event对象设为私有,类外通过公开的访问接口进行绑定,触发,解绑,这种方式起到了保护隐私的作用
- 定义
class ADelegateActor:public AActor { GENERATED_BODY() public: /* *OwingType:拥有此Event的类,本例中使用本类:ADelegateActor *EventName:事件名称 *ParamType:参数列表 */ DECLARE_EVENT_OneParam(ADelegateActor, MyDelegateEvent, FString); //公开对Event对象的访问接口 MyDelegateEvent& OnEventTrigger(){return DelegateEvent;} private: //将Event设为私有,防止类外直接访问到,起到安全作用 MyDelegateEvent DelegateEvent; }
- 绑定回调
class CallbackTarget { public: void FunctionForAddUObject(FString str); UFUNCTION() void FunctionForAddUFunction(FString str); //静态成员函数 static StaticCallback(FString str) { UE_LOG(LogTemp,Warning,TEXT("StaticCallback Call,Param Value:%s"),*str); } } //全局静态函数(静态非成员函数) void StaticFunc(FString str) { UE_LOG(LogTemp,Warning,TEXT("StaticFunc Call,Param Value:%s"),*str); }
CallbackTarget* Target=new CallbackTarget(); /*多播绑定回调函数以Add开头*/ //delegatehandle FDelegateHandle Handle; //AddUObject 绑定多播 Handle=OnEventTrigger().AddUObject(Target,&CallbackTarget::FunctionForAddUObject); //AddUFunction 绑定多播 Handle=OnEventTrigger().AddUFunction(Target,FName(TEXT("FunctionForAddUFunction"))); //AddStatic 绑定全局静态函数 Handle=OnEventTrigger().AddStatic(StaticFunc); //AddStatic 绑定静态成员函数 Handle=OnEventTrigger().AddStatic(&CallbackTarget::StaticCallback); //AddLambda 绑定Lambda表达式 Handle=OnEventTrigger().AddLambda( [](FString str) { UE_LOG(LogTemp,Warning,TEXT("Lambda Call,Param Value:%s"),*str); } )
- 触发和解绑
OnEventTrigger().Broadcast("DELEGATE EVENT Call"); OnEventTrigger().Clear();
继承的抽象事件
基础类实现:
/** Register/Unregister a callback for when assets are added to the registry */ DECLARE_EVENT_OneParam( IAssetRegistry, FAssetAddedEvent, const FAssetData&); virtual FAseetAddedEvent& OnAssetAdded() = 0;
派生类实现:
DECLARE_DERIVED_EVENT( FAssetRegistry, IAssetRegistry::FAssetAddedEvent, FAssetAddedEvent); virtual FassetAddedEvent& OnAssetAdded() override { return AssetAddedEvent; }
在派生类中声明一个派生事件时,不要在 DECLARE_DERIVED_EVENT 宏中重复函数签名。此外,DECLARE_DERIVED_EVENT 宏的最后一个参数是事件的新命名,通常与基础类型相同。
参考
-
官方文档
-
[UE4 C++入门到进阶] 4.Delegate(委托)
-
UE4 委托(代理)简单理解
-
UE4 delegate初探
-
UE4 C++学习笔记 -- 动态委托与蓝图交互
-
UE4-深入委托Delegate实现原理
这篇关于【UE4 C++】<6>Delegate 委托的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-15PingCAP 黄东旭参与 CCF 秀湖会议,共探开源教育未来
- 2024-05-13PingCAP 戴涛:构建面向未来的金融核心系统
- 2024-05-09flutter3.x_macos桌面os实战
- 2024-05-09Rust中的并发性:Sync 和 Send Traits
- 2024-05-08使用Ollama和OpenWebUI在CPU上玩转Meta Llama3-8B
- 2024-05-08完工标准(DoD)与验收条件(AC)究竟有什么不同?
- 2024-05-084万 star 的 NocoDB 在 sealos 上一键起,轻松把数据库编程智能表格
- 2024-05-08Mac 版Stable Diffusion WebUI的安装
- 2024-05-08解锁CodeGeeX智能问答中3项独有的隐藏技能
- 2024-05-08RAG算法优化+新增代码仓库支持,CodeGeeX的@repo功能效果提升