• 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基本没有区别!所以,不要用它来异步加载资源!

  • 相关阅读:
    FPGA结构分析——IDDR,网口储备点1
    cin处理用户异常输入
    【React路由】编程式路由导航和withRouter的使用——push replace
    vue中 axios get请求,使用formData传递参数给后台
    Linux文件权限
    UE5 官方案例Lyra 全特性详解 12.背包系统Inventory System 1添加物品到背包
    10、matlab中字符、数字、矩阵、字符串和元胞合并为字符串并将字符串以不同格式写入读出excel
    C++ 类和对象篇(六) 拷贝构造函数
    Spire.Office for Android 7.6.0
    Django--ORM 多表查询
  • 原文地址:https://blog.csdn.net/iaibeyond/article/details/127836385