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);
}
異步藍圖節點
使用方法
-
繼承 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; }