【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

posted @ 2021-05-28 13:53  砥才人  阅读(4471)  评论(0编辑  收藏  举报