在Unreal中使用LatentAction的几个步骤:
我们以Delay一定帧,并每帧返回当前剩余帧数来说明使用过程。
1、我们需要Delay函数能够有不同的输出节点,需要使用ExpandEnumAsExecs,首先定义输出节点的枚举类型
- UENUM(BlueprintType)
- enum class LatentExec :uint8
- {
- DelayExe,
- Complete,
- };
2、添加FPendingLatentAction子类
-
- class FRpgDelayForFramesLatentAction : public FPendingLatentAction
- {
- public:
- FRpgDelayForFramesLatentAction(const FLatentActionInfo& LatentInfo, int32 NumFrames,LatentExec& exec,int& CurFrame) : ExecutionFunction(LatentInfo.ExecutionFunction)
- , OutputLink(LatentInfo.Linkage)
- , CallbackTarget(LatentInfo.CallbackTarget)
- , FramesRemaining(NumFrames)
- , exec(exec)
- , CurFrame(CurFrame)
- {
- }
-
- virtual ~FRpgDelayForFramesLatentAction() {};
-
- virtual void UpdateOperation(FLatentResponse& Response) override
- {
- --FramesRemaining;
- if (FramesRemaining == 0)
- {
- exec = LatentExec::Complete;
- CurFrame = FramesRemaining;
- Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget);
- }
- else
- {
- exec = LatentExec::DelayExe;
- CurFrame = FramesRemaining;
- Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget);
- }
- }
-
- #if WITH_EDITOR
- FString GetDescription() const
- {
- return FString::Printf(TEXT("Delay (%d frames remaining)"), FramesRemaining);
- }
- #endif
- private:
- FName ExecutionFunction;
- int32 OutputLink;
- FWeakObjectPtr CallbackTarget;
- int32 FramesRemaining;
- LatentExec& exec;
- int& CurFrame;
- };
构造函数一般用来发起本次LatentAction,我们这里函数体没有代码,只需要记录一个延迟帧数即可。
UpdateOperation中更新剩余帧数,并在一定条件后触发不同的后续分支。
这里需要稍微注意一个TriggerLink和FinishAndTriggerLink两个函数。前者单纯地用来触发后续分支,后者判断条件满足后触发后续分支本结束本次LatentAction。
3、添加蓝图可用的Delay函数,这个可以加在任何一个BlueprintFunctionLibrary中
- UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "exec", Latent, LatentInfo = "LatentInfo"))
- static void DelayFramesLatent(int Frames, LatentExec& exec, FLatentActionInfo LatentInfo,int& CurFrame)
- {
- if (UWorld* World = GEngine->GetWorldFromContextObject(URpgGameInstance::GetInstance()->GetWorld(), EGetWorldErrorMode::LogAndReturnNull))
- {
- FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
- if (LatentActionManager.FindExistingAction
(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr) - {
- LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FRpgDelayForFramesLatentAction(LatentInfo, Frames, exec, CurFrame));
- }
- }
- }
4、可以在蓝图中使用了


这个例子中需要注意的几个点:
以上就是使用LatentAction的基本步骤,所以LatentAction能解决的事情是:每帧轮询(即执行某些轻度任务),在条件达成时,执行后续的蓝图节点。
LatentAction不能解决的事情是:不能异步加载资源,这个必须要记住!
一些同学看见LatentAction有异步的效果,认为可以使用它来解决资源的异步加载,但正如我们之前所说的,LatentAction的原理在于每一帧都进行条件轮询,条件完成再执行后续节点,它的UpdateOperation的调用是在主线程做的,并且每帧都会执行(直到条件满足)。用它来加载资源,和直接阻塞加载LoadSynchronous基本没有区别!所以,不要用它来异步加载资源!