【UE4 C++】Tick的三种方式、异步蓝图节点
阅读原文时间:2023年07月12日阅读:1

Tick的三种方式

  • 包括

    • 默认 Tick (Actor、Component、UMG)
    • TimerManager 定时器
    • FTickableGameObject
      • 可以写原生 Object
      • 也可以继承UObject 使用
  • 下面利用 AActor 直接实现三种 Tick

    class FTickableObject :public FTickableGameObject
    {
    public:
    bool bEnableTick = false;
    int32 TickCounter = 0;

    virtual void Tick(float DeltaTime)override {
        UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"\t [TickCounter]%d"), TickCounter);
    }
    virtual bool IsTickable()const override { return bEnableTick; }
    virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UTickableObject, STATGROUP_Tickables); }

    };

    UCLASS()
    class DESIGNPATTERNS_API AAsyncTickActor : public AActor
    {
    GENERATED_BODY()
    public:
    // Sets default values for this actor's properties
    AAsyncTickActor();

    // Called every frame
    virtual void Tick(float DeltaTime) override;
    void TimerTick();

    protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    private:
    int32 TickCounter = 0;
    FTimerHandle TimeHandle;
    TSharedPtr TickableObject;
    };

    AAsyncTickActor::AAsyncTickActor()
    {
    // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    }

    // Called when the game starts or when spawned
    void AAsyncTickActor::BeginPlay()
    {
    Super::BeginPlay();
    TickableObject = MakeShareable(new FTickableObject());
    TickableObject->bEnableTick = true;
    GetWorld()->GetTimerManager().SetTimer(TimeHandle, this, &AAsyncTickActor::TimerTick, 0.1f, true);
    }

    // Called every frame
    void AAsyncTickActor::Tick(float DeltaTime)
    {
    Super::Tick(DeltaTime);
    UE_LOG(LogTemp, Warning, TEXT(FUNCTION"\t[TickCounter]%d"), TickCounter);
    if (TickableObject.IsValid())
    {
    TickableObject->TickCounter = TickCounter;
    }

    }

    void AAsyncTickActor::TimerTick()
    {
    TickCounter++;
    UE_LOG(LogTemp, Warning, TEXT(FUNCTION"%\t [TickCounter]%d"), TickCounter);
    }


异步蓝图节点

  • 继承 UBlueprintAsyncActionBase

    • 成员函数 Activate 用于内部触发委托绑定的事件

    • 成员函数 RegisterWithGameInstance 允许将该对象注册到 GameInstance,防止GC

    • 注册后,调用成员方法 SetReadyToDestroy 销毁、释放,从 GameInstance 注销

    • 源码

      class UGameInstance;
      
      UCLASS()
      class ENGINE_API UBlueprintAsyncActionBase : public UObject
      {
          GENERATED_UCLASS_BODY()
      /** Called to trigger the action once the delegates have been bound */
      UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true"))
      virtual void Activate();
      
      /**
       * Call to globally register this object with a game instance, it will not be destroyed until SetReadyToDestroy is called
       * This allows having an action stay alive until SetReadyToDestroy is manually called, allowing it to be used inside loops or if the calling BP goes away
       */
      virtual void RegisterWithGameInstance(UObject* WorldContextObject);
      virtual void RegisterWithGameInstance(UGameInstance* GameInstance);
      
      /** Call when the action is completely done, this makes the action free to delete, and will unregister it with the game instance */
      virtual void SetReadyToDestroy();
      protected: TWeakObjectPtr<UGameInstance> RegisteredWithGameInstance; };
  • 声明静态函数

  • 需要动态多播委托,作为多个输出节点

  • 代码

    UCLASS()
    class DESIGNPATTERNS_API AAsyncTickActor : public AActor
    {
    public:
        UPROPERTY(BlueprintReadWrite)
            TArray<int32>CountdownNums;
    };
    
    // 定义委托类型
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCompleteHandleDelegate, int32, Result);
    
    UCLASS()
    class DESIGNPATTERNS_API UMyBPAsyncAction : public UBlueprintAsyncActionBase
    {
        GENERATED_BODY()
    public:
        UMyBPAsyncAction() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"")); }
        ~UMyBPAsyncAction() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__"")); }
    // 自定义的异步蓝图节点
    UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"), Category = "MyBPAsyncAction")
        static UMyBPAsyncAction* AsyncCountdown(UObject* WorldContextObject, AAsyncTickActor* AsyncTickActor, int32 StartNum);
    public: //输出节点 UPROPERTY(BlueprintAssignable) FCompleteHandleDelegate OnSucceeded;
    //输出节点
    UPROPERTY(BlueprintAssignable)
        FCompleteHandleDelegate OnFailed;
    private: FTimerHandle TimerHandle; protected: virtual void Activate() override; }; void UMyBPAsyncAction::Activate() { // 开新的线程,测试 Activate 调用情况 Async(EAsyncExecution::ThreadPool, [&]() { UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Start Delay 1s.")); FPlatformProcess::Sleep(1.0f); UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Delay Finished.")); }); } UMyBPAsyncAction* UMyBPAsyncAction::AsyncCountdown(UObject* WorldContextObject, AAsyncTickActor* AsyncTickActor, int32 StartNum) { if (WorldContextObject == nullptr) { FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject"), ELogVerbosity::Error); UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Invalid WorldContextObject.")); return nullptr; }
    UMyBPAsyncAction* MyBPAsyncActionNode = NewObject&lt;UMyBPAsyncAction&gt;();
    // Lambda 表达式
    auto CountdownFunc = [&amp;,MyBPAsyncActionNode, WorldContextObject, AsyncTickActor, StartNum]() {
        if (IsValid(AsyncTickActor))
        {
            if (AsyncTickActor-&gt;CountdownNums.Num() == StartNum)
            {
                // OnSucceeded输出节点
                MyBPAsyncActionNode-&gt;OnSucceeded.Broadcast(1);  
    
                // 清空定时器
                WorldContextObject-&gt;GetWorld()-&gt;GetTimerManager().ClearTimer(MyBPAsyncActionNode-&gt;TimerHandle);
    
                // 如果不使用,则销毁
                MyBPAsyncActionNode-&gt;SetReadyToDestroy();
                UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Countdown Completed."));
            }
            else
            {
                int32 length = AsyncTickActor-&gt;CountdownNums.Num();
                AsyncTickActor-&gt;CountdownNums.Add(length + 1);
                UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Counting down... %d"), length + 1);
            }
        }
        else
        {
            // OnFailed 输出节点
            MyBPAsyncActionNode-&gt;OnFailed.Broadcast(-1);
            WorldContextObject-&gt;GetWorld()-&gt;GetTimerManager().ClearTimer(MyBPAsyncActionNode-&gt;TimerHandle);
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Invalid AsyncTickActor."));
            FFrame::KismetExecutionMessage(TEXT("Invalid AsyncTickActor"), ELogVerbosity::Error);
        }
    };
    
    // 设置定时器并开始
    WorldContextObject-&gt;GetWorld()-&gt;GetTimerManager().SetTimer(MyBPAsyncActionNode-&gt;TimerHandle, FTimerDelegate::CreateLambda(CountdownFunc), 1.0f, true);
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Countdown Starting."));
    return MyBPAsyncActionNode;
    }

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章