【UE4 C++】Tick的三種方式、異步藍圖節點、Latent Action


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<FTickableObject> 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);
}

異步藍圖節點

image

使用方法

  • 繼承 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<UMyBPAsyncAction>();
    	// Lambda 表達式
    	auto CountdownFunc = [&,MyBPAsyncActionNode, WorldContextObject, AsyncTickActor, StartNum]() {
    		if (IsValid(AsyncTickActor))
    		{
    			if (AsyncTickActor->CountdownNums.Num() == StartNum)
    			{
    				// OnSucceeded輸出節點
    				MyBPAsyncActionNode->OnSucceeded.Broadcast(1);	
    				
    				// 清空定時器
    				WorldContextObject->GetWorld()->GetTimerManager().ClearTimer(MyBPAsyncActionNode->TimerHandle);
    				
    				// 如果不使用,則銷毀
    				MyBPAsyncActionNode->SetReadyToDestroy();
    				UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Countdown Completed."));
    			}
    			else
    			{
    				int32 length = AsyncTickActor->CountdownNums.Num();
    				AsyncTickActor->CountdownNums.Add(length + 1);
    				UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Counting down... %d"), length + 1);
    			}
    		}
    		else
    		{
    			// OnFailed 輸出節點
    			MyBPAsyncActionNode->OnFailed.Broadcast(-1);
    			WorldContextObject->GetWorld()->GetTimerManager().ClearTimer(MyBPAsyncActionNode->TimerHandle);
    			UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Invalid AsyncTickActor."));
    			FFrame::KismetExecutionMessage(TEXT("Invalid AsyncTickActor"), ELogVerbosity::Error);
    		}
    	};
    	
    	// 設置定時器並開始
    	WorldContextObject->GetWorld()->GetTimerManager().SetTimer(MyBPAsyncActionNode->TimerHandle, FTimerDelegate::CreateLambda(CountdownFunc), 1.0f, true);
    	UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" Countdown Starting."));
    	return MyBPAsyncActionNode;
    }
    

    image


Latent Action


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM