摘要:函数的异步调用是编程过程中一种常见的调用方式,虽然虚幻引擎拥有强大的蓝图系统,但是在处理异步调用时,现有的蓝图节点却常常束手无策。本教程旨讲解在ue4中设计并实现异步蓝图节点的全流程,以实现具有计时、可取消延迟Delay、同时处理N个对象的异步调用等多个功能的异步蓝图节点SetTimer为例,逐一解析异蓝图节点从需求、设计、实现、应用各个过程。
关键字:虚幻引擎;蓝图;异步;定时器
Chapter 8 Design and Implementation of Asynchronous Blueprint Node
Abstract:Asynchronous calling of functions is a common calling way in the programming process. Although Unreal Engine has a powerful blueprint system, the existing blueprint nodes are often at a loss for handling asynchronous call processes. This tutorial aims to explain the whole process of designing and implementing asynchronous blueprint nodes in ue4.Taking the custom asynchronous blueprint node SetTimer, which has the functions of timing, delay, and asynchronous call of N objects at the same time, as an example. Analyze each process of asynchronous blueprint nodes from requirements, design, implementation, and application one by one.
Key words: Unreal Engine; Blueprint; Async; Timer
1. Introduction
使用ue4开发时,常常会遇到异步调用情形;在上一个函数A执行结束,才触发下一个函数B,且函数A是一个多帧执行的持续过程,比如移动到目标点。若函数A在执行时被中止,则函数B不执行,换言之函数B应该是可被取消的延迟调用。对于单个对象的上述情形,使用蓝图系统中Set Timer By Event节点不难解决;若是同时处理N个不同类型对象的异步调用,则内置的蓝图节点难以应对。
利用ue4蓝图系统中内置节点完成简单异步调用包含以下方式:Delay:等待一定时间,再执行下一个函数;缺点: Delay未结束前,Delay之后的逻辑被阻塞;且无法取消Delay后面的事件;
Timeline:Timeline功能较为完善,包含Play、Stop、Reverse等功能;缺点:只能在Actor及其派生类中使用,无法在Object、UMG等其他类蓝图中使用;无法动态添加;
SetTimerByEvent / SetTimerByFunctionName:支持绑定事件,支持Pause、Unpause、Clear,计时等功能;缺点:无法同时处理多个对象的异步过程。
Tick:Tick时,执行多帧持续性的逻辑;满足结束条件后,调用回调事件逻辑;缺点:需要独立封装。
利用ue4 c++中实现异步蓝图函数包含以下方式:使用UFunction宏中的Latent关键字,标识该蓝图函数为Latent function(潜伏事件),在函数体内创建一个FPendingLatentAction派生类的实例并初始化,异步操作的主要过程均在派生类实例上完成。内置蓝图节点的Delay、MoveComponentTo等均采用这类方法。
继承UBlueprintAsyncActionBase类,重写派生类的函数;内置蓝图节点DownloadImage节点属于此类。
Tick和蓝图Tick一样,需要单独封装。
ue4的异步与非异步的蓝图节点在外观上有一个明显的不同,就是异步蓝图节点右上角存时钟标记;使用时,异步蓝图节点可以在EventGraph/Macros中使用,但是无法在蓝图函数(FunctionGraph)中使用。由于异步节点被调用时,可能同时存在多个任务,所以与一般的蓝图节点只有一对输入输出执行流引脚不同,异步节点可能拥有多个执行流引脚;一对执行流引脚用于继续执行主逻辑,其他执行流引脚用于处理异步过程结束后的返回结果,这样而不至于阻塞程序主要逻辑的执行。异步和非异步蓝图节点
2. Design
由上一小节不难得出结论:[需求]若要同时处理N个对象的异步调用,引擎内置的蓝图节点是难以满足需求的,因此设计并实现一个具有这样功能的蓝图节点是十分必要的。Set Timer By Event支持Pause、Unpause、Clear等功能,也可以等效地被封装成一个可取消的Delay节点;但是由于SetTimerByEevent节点的Delegate是一个无参数的委托,因此在处理N个对象的时候,无法确定是哪一个对象完成了Delay过程。[设计]假设我们把Set Timer By Event节点的左侧输入引脚Event,移到节点右侧,变成一个输出执行流引脚Completed;并且在执行Completed引脚后的函数时,可以同时返回TimerHandle,那么就可以根据TimerHandle取得与之对应的Object,然后调用该Object的函数,完成同时处理N个对象的异步调用。在经过以上调整,新SetTimer节点的引脚应该是如下结构。SetTimer节点的引脚
在同时处理N个对象的异步调用时,每个对象的异步过程执行时间和间隔都可以是独立的,使用以下蓝图节点处理N个对象异步过程。同时处理N个对象的异步调用简易方案
3. Source code
[实现]源代码如下所示,在去除注释和空行,余下不过70+行,其中特别需要注意的是GC过程。首先贴出源代码,随后解释代码中各个部分的作用。
//.h 文件