• Unreal&LatentAction的能与不能


    Unreal中使用LatentAction的几个步骤:

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

    我们以Delay一定帧,并每帧返回当前剩余帧数来说明使用过程。

    1、我们需要Delay函数能够有不同的输出节点,需要使用ExpandEnumAsExecs,首先定义输出节点的枚举类型

    1. UENUM(BlueprintType)
    2. enum class LatentExec :uint8
    3. {
    4. DelayExe,
    5. Complete,
    6. };

    2、添加FPendingLatentAction子类

    1. class FRpgDelayForFramesLatentAction : public FPendingLatentAction
    2. {
    3. public:
    4. FRpgDelayForFramesLatentAction(const FLatentActionInfo& LatentInfo, int32 NumFrames,LatentExec& exec,int& CurFrame) : ExecutionFunction(LatentInfo.ExecutionFunction)
    5. , OutputLink(LatentInfo.Linkage)
    6. , CallbackTarget(LatentInfo.CallbackTarget)
    7. , FramesRemaining(NumFrames)
    8. , exec(exec)
    9. , CurFrame(CurFrame)
    10. {
    11. }
    12. virtual ~FRpgDelayForFramesLatentAction() {};
    13. virtual void UpdateOperation(FLatentResponse& Response) override
    14. {
    15. --FramesRemaining;
    16. if (FramesRemaining == 0)
    17. {
    18. exec = LatentExec::Complete;
    19. CurFrame = FramesRemaining;
    20. Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget);
    21. }
    22. else
    23. {
    24. exec = LatentExec::DelayExe;
    25. CurFrame = FramesRemaining;
    26. Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget);
    27. }
    28. }
    29. #if WITH_EDITOR
    30. FString GetDescription() const
    31. {
    32. return FString::Printf(TEXT("Delay (%d frames remaining)"), FramesRemaining);
    33. }
    34. #endif
    35. private:
    36. FName ExecutionFunction;
    37. int32 OutputLink;
    38. FWeakObjectPtr CallbackTarget;
    39. int32 FramesRemaining;
    40. LatentExec& exec;
    41. int& CurFrame;
    42. };

    构造函数一般用来发起本次LatentAction,我们这里函数体没有代码,只需要记录一个延迟帧数即可。

    UpdateOperation中更新剩余帧数,并在一定条件后触发不同的后续分支。

    这里需要稍微注意一个TriggerLink和FinishAndTriggerLink两个函数。前者单纯地用来触发后续分支,后者判断条件满足后触发后续分支本结束本次LatentAction。

    3、添加蓝图可用的Delay函数,这个可以加在任何一个BlueprintFunctionLibrary中

    1. UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "exec", Latent, LatentInfo = "LatentInfo"))
    2. static void DelayFramesLatent(int Frames, LatentExec& exec, FLatentActionInfo LatentInfo,int& CurFrame)
    3. {
    4. if (UWorld* World = GEngine->GetWorldFromContextObject(URpgGameInstance::GetInstance()->GetWorld(), EGetWorldErrorMode::LogAndReturnNull))
    5. {
    6. FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
    7. if (LatentActionManager.FindExistingAction(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr)
    8. {
    9. LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FRpgDelayForFramesLatentAction(LatentInfo, Frames, exec, CurFrame));
    10. }
    11. }
    12. }

    4、可以在蓝图中使用了

     

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

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

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

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

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

  • 相关阅读:
    Intellij IDEA 2022 正式发布,这些功能真不错
    腾讯测试大鸟分享4个关于 Python 函数(方法)的冷知识
    CTF PWN 中常用的工具安装【Ubuntu 20.04】
    SQL语言
    学的Java要去 IT 外包公司?
    【顶顶通呼叫中心中间件(mod_cti 基于 FreeSWITCH)-拨号方案和路由配置】
    unity快速入门代码上手笔记
    卸载重装最新版mysql数据库亲测有效
    Android 获取安装的app
    智能视频监控平台EasyCVR如何使用接口批量导出iframe地址?
  • 原文地址:https://blog.csdn.net/iaibeyond/article/details/127836385