UE4 C++:Delegate委托

目录

概念

定义

声明委托 

单播委托

声明宏

绑定委托

执行函数

用法示例

不带参数Delegate

带参数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);
  • BindObject:绑定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,从而暴露给蓝图使用,其他代码均无法使用
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FNotifyPawnChange, float, PawnHpPercent,float,PawnPhysicalShieldPercent,float, PawnMageShieldPercent);

UPROPERTY(BlueprintAssignable)
	FNotifyPawnChange NotifyPawnChange;

NotifyPawnChange.Broadcast(PawnHpPercent, PawnPhysicalShieldPercent, PawnMageShieldPercent);

 

事件

事件与组播委托十分相似,虽然任意类均可绑定事件,但只有声明事件的类可以调用事件的Broadcst、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++ 基础知识】<8> Delegate 委托 - 砥才人 - 博客园 (cnblogs.com)

委托 | 虚幻引擎5.0文档 (unrealengine.com)

[UE4 C++入门到进阶] 4.Delegate(委托) - 哔哩哔哩 (bilibili.com)

UE4 delegate初探 - 知乎 (zhihu.com)

UE4-深入委托Delegate实现原理 - 知乎 (zhihu.com)

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值