【UE4 C++ 基础知识】<8> 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
检查一个委托是否已绑定,如是,则调用ExecuteIsBound
检查一个委托是否已绑定,经常出现在包含 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
检查一个动态委托是否已绑定,如是,则调用ExecuteIsBound
检查一个动态委托是否已绑定,经常出现在包含 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);
事件
事件与 组播委托 十分相似。虽然任意类均可绑定事件,但只有声明事件的类可以调用事件 的 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++ 基础知识】<8> Delegate 委托的更多相关文章
- 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载
同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...
- 【UE4 C++ 基础知识】<3> 基本数据类型、字符串处理及转换
基本数据类型 TCHAR TCHAR就是UE4通过对char和wchar_t的封装 char ANSI编码 wchar_t 宽字符的Unicode编码 使用 TEXT() 宏包裹作为字面值 TCHAR ...
- 【UE4 C++ 基础知识】<12> 多线程——FRunnable
概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...
- C#基础知识六之委托(delegate、Action、Func、predicate)
1. 什么是委托 官方解释 委托是定义方法签名的类型,当实例化委托时,您可以将其实例化与任何具有兼容签名的方法想关联,可以通过委托实例调用方法. 个人理解 委托通俗一点说就是把一件事情交给别人来帮助完 ...
- C# 基础知识系列- 11 委托和事件
0. 前言 事件和委托是C#中的高级特性,也是C#中很有意思的一部分.出现事件的地方,必然有委托出现:而委托则不一定会有事件出现.那为什么会出现这样的关系呢?这就需要从事件和委托的定义出发,了解其中的 ...
- 【UE4 C++ 基础知识】<2> UFUNCTION宏、函数说明符、元数据说明符
UFunction声明 UFunction 是虚幻引擎4(UE4)反射系统可识别的C++函数.UObject 或蓝图函数库可将成员函数声明为UFunction,方法是将 UFUNCTION 宏放在头文 ...
- 【UE4 C++ 基础知识】<13> 多线程——TaskGraph
概述 TaskGraph 系统是UE4一套抽象的异步任务处理系统 TaskGraph 可以看作一种"基于任务的并行编程"设计思想下的实现 通过TaskGraph ,可以创建任意多线 ...
- 【UE4 C++ 基础知识】<1> UPROPERTY宏、属性说明符、元数据说明符
属性声明 属性使用标准的C++变量语法声明,前面用UPROPERTY宏来定义属性元数据和变量说明符. UPROPERTY([specifier, specifier, ...], [meta(key= ...
- 【UE4 C++ 基础知识】<4> 枚举 Enum、结构体 Struct
枚举 UENUM宏搭配BlueprintType可以将枚举暴露给蓝图,不使用的话,仅能在C++使用 //定义一个原生enum class enum class EMyType { Type1, Typ ...
随机推荐
- 将数据保存到excel文件(纯前端实现)
// 导出excel文件 /** * 依赖: import XLSX from 'xlsx' */ let obj = { '学生信息表': [ ['姓名', '性别', '年龄', '分数'], [ ...
- (二)Superset 1.3图表篇——Time-series Table
(二)Superset 1.3图表篇--Time-series Table 本系列文章基于Superset 1.3.0版本.1.3.0版本目前支持分布,趋势,地理等等类型共59张图表.本次1.3版本的 ...
- Django——Paginator分页功能练习
1.路由urls.py from django.contrib import admin from django.urls import path from app01.views import in ...
- 比培训机构还详细的 Python 学习路线,你信吗 0^0
前言 这其实是将自己写的文章进行一个总结分类,并不代表最佳学习路线 会不断更新这篇文章...没链接的文章正在编写ing...会不会哪天我的这个目录就出现在培训机构的目录上了... 目前实战比较少(要是 ...
- Linu常用日志分析实战
日志结构分析 分析日志状态码所在位置为第九个 遍历取出第一行日志的每个字段 //取出第一行日志 awk 'NR==1{for(i=1;i<=NF;i++)print i"= " ...
- LVS负载均衡集群--NAT模式部署
目录: 一.企业群集应用概述 二.负载均衡群集架构 三.负载均衡群集工作模式分析 四.关于LVS虚拟服务器 五.NAT模式 LVS负载均衡群集部署 一.企业群集应用概述 1.群集的含义 Cluster ...
- vue-cli3项目中使用vue-ueditor-wrap
Vue + UEditor + v-model 双向绑定 一.安装 1 npm i vue-ueditor-wrap 2 # 或者 3 yarn add vue-ueditor-wrap 二.下载文件 ...
- C# .NET Core 3.1 中 AssemblyLoadContext 的基本使用
C# .NET Core 3.1 中 AssemblyLoadContext 的基本使用 前言 之前使用 AppDomain 写过一个动态加载和释放程序的案例,基本实现了自己"兔死狗烹&qu ...
- scrapy各种持久化存储的奇淫技巧
理论 磁盘文件: 基于终端指令 1)保证parse方法返回一个可迭代类型的对象(存储解析到的页面内容) 2)使用终端指令完成数据存储到指定磁盘文件中的操作,如:scrapy crawl 爬虫文件名称 ...
- PHP中的MySQLi扩展学习(四)mysqli的事务与预处理语句
对于 MySQLi 来说,事务和预处理语句当然是它之所以能够淘汰 MySQL(原始) 扩展的资本.我们之前也已经学习过了 PDO 中关于事务和预处理语句相关的内容.所以在这里,我们就不再多讲理论方面的 ...