Unreal&LatentAction的能与不能

23 篇文章 12 订阅

在Unreal中使用LatentAction的几个步骤:

  1. 继承FPendingLatentAction,派生出子类,构造函数传入需要LatentAction涉及到的参数。
  2. 覆写UpdateOperation函数,实现每帧调用的逻辑。
  3. UpdateOperation内部判断当前条件,如果需要结束,则调用FLatentResponse::FinishAndTriggerIf或者FLatentResponse::DoneIf。

我们以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<FRpgDelayForFramesLatentAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr)
		{
			LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FRpgDelayForFramesLatentAction(LatentInfo, Frames, exec, CurFrame));
		}
	}
}

4、可以在蓝图中使用了

 

这个例子中需要注意的几个点:

  1. 覆写FPendingLatentAction::UpdateOperation时要记住,这个函数是每帧执行一次的,如果函数的任务过重,会使主线程卡顿。
  2. LatentAction必须要两部分结合使用,即继承自FPendingLatentAction的子类,和带有Latent标识的C++函数。
  3. 多分支的原理在于ExpandEnumAsExecs,并且LatentAction内部的枚举类型是引用,这使得它可以控制函数的ExpandEnumAsExecs参数,进而控制分支出口。同理用来控制当前帧的输出参数CurFrame。
  4. GetDescription会在节点上显示文本,用来在编辑器中辅助调试。

以上就是使用LatentAction的基本步骤,所以LatentAction能解决的事情是:每帧轮询(即执行某些轻度任务),在条件达成时,执行后续的蓝图节点。

 LatentAction不能解决的事情是:不能异步加载资源,这个必须要记住!

一些同学看见LatentAction有异步的效果,认为可以使用它来解决资源的异步加载,但正如我们之前所说的,LatentAction的原理在于每一帧都进行条件轮询,条件完成再执行后续节点,它的UpdateOperation的调用是在主线程做的,并且每帧都会执行(直到条件满足)。用它来加载资源,和直接阻塞加载LoadSynchronous基本没有区别!所以,不要用它来异步加载资源!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值