前言
由 Epic Games 推出的虚幻系列引擎,因其高效、全能、易获取、所见即所得等特性受到广大游戏开发者的欢迎,市面上也不乏从入门到深度分析的教程。本系列主要面向虚幻引擎的初学者以及有一定实践经验的虚幻引擎游戏开发者,分享能够立即运用在自己项目中的实践技巧。本教程综合了个人的学习笔记、官方文档以及个人心得,水平不足之处,望读者反馈和指正。
本文是 UE 应用实例分享系列专栏的第二篇。
什么是委托
按字面意思理解,委托(Delegate)就是把一段逻辑“委托”给另一对象来执行。好比你有一个控制玩家生命值的 Manager 对象,还有一个 UI 对象用来显示玩家生命值。这时,玩家受到伤害,生命值下降了,UI 却还不知道 Manager 里的值已经发生了变化,简单粗暴的做法是,让 UI 对象每一帧都去 Manager 看看生命值是不是变化了。如果使用委托,Manger 只需要在生命值改变时触发「OnHPChange」委托事件,而 UI 只需要在初始化的时候绑定一下这个委托即可。
总结来说,委托是一种由事件驱动的多对象间的异步通讯机制。
使用委托是性能更优的选择:我们可以不必在 Tick 函数的每一帧去判断是否执行某些逻辑,而使其基于事件来触发。
委托的类型
委托(Delegates)可以以安全的方式调用函数,虚幻引擎提供了三种类型的委托:
如何使用委托
使用委托分为以下几步:
- 使用引擎提供的宏声明一个委托类型
- 定义一个刚刚声明类型的委托对象
- 绑定对应的函数到这个委托上
- 在合适的地方触发委托事件
如何声明委托
虚幻引擎定义了一套宏来给我们用于声明委托类型。
普通的委托
// 没有参数的委托,最简单的形式 DECLARE_DELEGATE(DelegateName) // 带一个参数的委托, DECLARE_DELEGATE_OneParam(DelegateName, Param1Type) // 带两个参数的委托 DECLARE_DELEGATE_TwoParams(DelegateName, Param1Type, Param2Type) // 以此类推可以带多个参数 DECLARE_DELEGATE_Params(DelegateName, Param1Type, Param2Type, ...)
带有返回值的委托
// 没有参数的 DECLARE_DELEGATE_RetVal(RetValType, DelegateName) // 带有参数的 DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type) DECLARE_DELEGATE_RetVal_TwoParams(RetValType, DelegateName, Param1Type, Param2Type) DECLARE_DELEGATE_RetVal_Params(RetValType, DelegateName, Param1Type, Param2Type, ...)
动态委托(Dynamic)与多播委托(Multicast)
DECLARE_MULTICAST_DELEGATE... DECLARE_DYNAMIC_DELEGATE... DECLARE_DYNAMIC_MULTICAST_DELEGATE... DECLARE_DYNAMIC_DELEGATE... DECLARE_DYNAMIC_MULTICAST_DELEGATE...
动态委托
动态委托(可以在运行时动态的绑定和解绑函数)可以按名字查找,可以被序列化,但相较普通委托,执行速度较慢。
声明
DECLARE_DYNAMIC_DELEGATE[_RetVal, ...]( DelegateName )
DECLARE_DYNAMIC_MULTICAST_DELEGATE[_RetVal, ...]( DelegateName )
注意动态委托的声明在每一个类型之后还有一个额外参数需要给出这个类型的变量名。
绑定
BindDynamic( UserObject, FuncName )
针对单播的委托绑定AddDynamic( UserObject, FuncName )
对于 Multi-cast Delegates 的动态委托使用它来进行绑定RemoveDynamic( UserObject, FuncName )
移除绑定
执行
函数 | 说明 |
---|---|
IsBound | 检查是否绑定一个委托 |
Execute | 不检查绑定而执行委托 |
ExecuteIfBound | 当绑定一个委托时,执行调用 |
多播委托
多播委托可以绑定多个函数,不能有返回值。
声明 Multi-Cast Delegates
DECLARE_MULTICAST_DELEGATE[_RetVal, ...]( DelegateName )
DECLARE_DYNAMIC_MULTICAST_DELEGATE[_RetVal, ...]( DelegateName )
绑定 Multi-Cast Delegates
Add()
绑定单个函数AddStatic()
绑定 C++ 原始全局函数指针AddRaw()
绑定 C++ 原始指针函数AddSP()
添加基于共享指针(快速,非线程安全)的成员函数委托AddUObject()
添加基于 UObject 的成员函数委托Remove()
移除单个绑定的函数RemoveAll()
移除全部
执行所有绑定的函数
Broadcast()
广播此委托到所有绑定的函数并执行,执行顺序是不确定的
动态多播委托
可以暴露给蓝图使用的委托,非常常用。声明动态多播委托需要在委托对象上添加 BlueprintAssignable 属性。
DECLARE_DYNAMIC_MULTICAST_DELEGATE(DelegateName); DECLARE_DYNAMIC_MULTICAST_DELEGATE_ONEPARAM(DelegateName, Param1Type, Param1Name); DECLARE_DYNAMIC_MULTICAST_DELEGATE_XXXPARAM(DelegateName, Param1Type, Param1Name,...);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyDelegate, class AActor*, MyActor); UPROPERTY(BlueprintAssignable) FMyDelegate MyDelegate;
- 调用
Broadcast()
可以执行动态多播委托。
示例:使用委托进行碰撞检测
为了更好的说明委托的用法,这里结合碰撞检测的实现方式进行一个示例讲解。
- 引擎声明的委托类型 [1]
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SixParams( FComponentBeginOverlapSignature, UprimitiveComponent, OnComponentBeginOverlap, UprimitiveComponent, OverlappedComponent, // 第一个参数 AActor*, OtherActor, // 第二个参数 UprimitiveComponent*, OtherComponent, // 第三个参数 int32, OtherBodyIndex, // 第四个参数 bool, bFromSweep, // 第五个参数 const FHitResult, SweepResult // 第六个参数 )
- 引擎定义的委托类型
FComponentBeginOverlapSignature OnComponentBeginOverlap;
- 定义碰撞处理函数
UFUNCTION() void FunctionName( UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult ); // .cpp file implementation
- 在要调用碰撞事件的组件上绑定委托
// BeginPlay AnyComp->OnComponentBeginOverlap.AddDynamic(this, &AClassName::FunctionName);
- 调用委托
OnComponentBeginOverlap.Boardcast();
[1] SPARSE Delegate: It’s a memory optimisation for delegates that are almost never bound.
封面:UE 启动页面
*本文内容系作者独立观点,不代表 indienova 立场。未经授权允许,请勿转载。
暂无关于此文章的评论。