Created
December 2, 2021 15:18
-
-
Save vgvgvvv/63339908137485cbd640c8f77d2a4eb2 to your computer and use it in GitHub Desktop.
ActionQueue For Unreal Engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ActionQueue.h" | |
#include "TimerManager.h" | |
#include "Engine/World.h" | |
DEFINE_STAT(STAT_NumActionQueue); | |
int GUseActionQueue = 1; | |
static FAutoConsoleVariableRef CVarGUseActionQueue( | |
TEXT("actionqueue.UseActionQueue"), | |
GUseActionQueue, | |
TEXT("If Use ActionQueue"), | |
ECVF_Default); | |
TFunction<void(FActionQueue*)> FActionQueue::ReleaseFunction = [](FActionQueue* Self) { delete Self; }; | |
//直接创建 | |
TFunction<FActionQueue* ()> FActionQueue::CreateFunction = []() { return new FActionQueue; }; | |
//用于复用 | |
TFunction<FActionQueue* ()> FActionQueue::GetFunction = []() { return FActionQueue::Create(); }; | |
static int ActionQueueCount = 0; | |
FActionQueue::FCommand::FCommand(): Callback(nullptr), OnCancel(nullptr) | |
{ | |
} | |
FActionQueue::FCommand:: | |
FCommand(FActionQueue::Callback Callback, CommandCancelAction OnCancel): Callback(Callback), OnCancel(OnCancel) | |
{ | |
} | |
FActionQueue* FActionQueue::Get(FString DebugName) | |
{ | |
auto Result = GetFunction(); | |
#if WITH_EDITORONLY_DATA | |
Result->DebugName = DebugName; | |
#endif | |
return Result; | |
} | |
FActionQueue* FActionQueue::Create(FString DebugName) | |
{ | |
INC_DWORD_STAT(STAT_NumActionQueue); | |
ActionQueueCount++; | |
auto Result = CreateFunction(); | |
Result->IsRunning = false; | |
#if WITH_EDITORONLY_DATA | |
Result->DebugName = DebugName; | |
#endif | |
return Result; | |
} | |
FActionQueue* FActionQueue::AddCallbackAction(CommandActionWithCallback Action, CommandCancelAction OnCancel) | |
{ | |
if (GUseActionQueue) | |
{ | |
if (IsRunning) | |
{ | |
ActionQueueInternal.Enqueue( | |
FCommand([this, Action]() | |
{ | |
WaitAndDo(Action); | |
}, OnCancel) | |
); | |
} | |
else | |
{ | |
IsRunning = true; | |
CurrentCancelHandler = OnCancel; | |
WaitAndDo(Action); | |
} | |
}else | |
{ | |
Action([](){}); | |
} | |
return this; | |
} | |
FActionQueue* FActionQueue::AddNoCallbackAction(CommandActionNoCallback Action, CommandCancelAction OnCancel) | |
{ | |
if (GUseActionQueue) | |
{ | |
if (IsRunning) | |
{ | |
ActionQueueInternal.Enqueue(FCommand([this, Action]() | |
{ | |
WaitAndDoNoCallback(Action); | |
}, OnCancel)); | |
} | |
else | |
{ | |
IsRunning = true; | |
CurrentCancelHandler = OnCancel; | |
WaitAndDoNoCallback(Action); | |
} | |
}else | |
{ | |
Action(); | |
} | |
return this; | |
} | |
void FActionQueue::SetReleaseFunction(TFunction<void(FActionQueue*)> Function) | |
{ | |
ReleaseFunction = Function; | |
} | |
void FActionQueue::SetCreateFunction(TFunction<FActionQueue*()> Function) | |
{ | |
CreateFunction = Function; | |
} | |
void FActionQueue::SetGetFunction(TFunction<FActionQueue* ()> Function) | |
{ | |
GetFunction = Function; | |
} | |
FActionQueue* FActionQueue::Wait(float Seconds) | |
{ | |
if(GUseActionQueue) | |
{ | |
AddCallbackAction([Seconds, this](Callback Callback) | |
{ | |
GWorld->GetTimerManager().SetTimer(TimerHandle, [this, Callback, Seconds]() | |
{ | |
if (IsRunning) | |
{ | |
Callback(); | |
} | |
}, Seconds, false, Seconds); | |
}, [this]() | |
{ | |
GWorld->GetTimerManager().ClearTimer(TimerHandle); | |
}); | |
} | |
return this; | |
} | |
FActionQueue* FActionQueue::WaitForNextTick() | |
{ | |
if (GUseActionQueue) | |
{ | |
AddCallbackAction([this](Callback Callback) | |
{ | |
GWorld->GetTimerManager().SetTimerForNextTick([this, Callback] | |
{ | |
if(IsRunning) | |
{ | |
Callback(); | |
} | |
}); | |
}, [this]() | |
{ | |
GWorld->GetTimerManager().ClearTimer(TimerHandle); | |
}); | |
} | |
return this; | |
} | |
FActionQueue* FActionQueue::AutoDestroy() | |
{ | |
if (GUseActionQueue) | |
{ | |
AddCallbackAction([this](Callback Callback) | |
{ | |
if (ActionQueueInternal.IsEmpty()) | |
{ | |
DEC_DWORD_STAT(STAT_NumActionQueue) | |
ActionQueueCount--; | |
GWorld->GetTimerManager().ClearTimer(TimerHandle); | |
ReleaseFunction(this); | |
} | |
else | |
{ | |
AutoDestroy(); | |
} | |
}); | |
return this; | |
}else | |
{ | |
ReleaseFunction(this); | |
return nullptr; | |
} | |
} | |
void FActionQueue::Clean() | |
{ | |
if (GUseActionQueue) | |
{ | |
if (CurrentCancelHandler != nullptr) | |
{ | |
CurrentCancelHandler(); | |
} | |
CurrentCancelHandler = nullptr; | |
ActionQueueInternal.Empty(); | |
IsRunning = false; | |
} | |
} | |
FActionQueue::FActionQueue() | |
{ | |
IsRunning = false; | |
} | |
FActionQueue::~FActionQueue() | |
{ | |
} | |
void FActionQueue::WaitAndDo(CommandActionWithCallback Action) | |
{ | |
Action([this]() | |
{ | |
CurrentCancelHandler = nullptr; | |
if(IsRunning) | |
{ | |
TryDequeue(); | |
}else | |
{ | |
Clean(); | |
} | |
}); | |
} | |
void FActionQueue::WaitAndDoNoCallback(CommandActionNoCallback Action) | |
{ | |
Action(); | |
CurrentCancelHandler = nullptr; | |
if (IsRunning) | |
{ | |
TryDequeue(); | |
}else | |
{ | |
Clean(); | |
} | |
} | |
void FActionQueue::TryDequeue() | |
{ | |
if(this == nullptr) | |
{ | |
return; | |
} | |
if (!ActionQueueInternal.IsEmpty()) | |
{ | |
FCommand Command; | |
ActionQueueInternal.Dequeue(Command); | |
ensure(Command.Callback); | |
CurrentCancelHandler = Command.OnCancel; | |
Command.Callback(); | |
} | |
else | |
{ | |
IsRunning = false; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "Queue.h" | |
#include "Function.h" | |
#include "Engine/EngineTypes.h" | |
DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Action Queue Count"), STAT_NumActionQueue, STATGROUP_Game, ENGINE_API); | |
class ENGINE_API FActionQueue | |
{ | |
public: | |
typedef TFunction<void ()> Callback; | |
typedef TFunction<void (Callback)> CommandActionWithCallback; | |
typedef TFunction<void ()> CommandActionNoCallback; | |
typedef TFunction<void ()> CommandCancelAction; | |
typedef TFunction<bool ()> Condition; | |
class FCommand | |
{ | |
public: | |
FCommand(); | |
FCommand(Callback Callback, CommandCancelAction OnCancel); | |
Callback Callback; | |
CommandCancelAction OnCancel; | |
}; | |
public: | |
static FActionQueue* Get(FString DebugName = ""); | |
static FActionQueue* Create(FString DebugName = ""); | |
FActionQueue* AddCallbackAction(CommandActionWithCallback Action, CommandCancelAction OnCancel = nullptr); | |
FActionQueue* AddNoCallbackAction(CommandActionNoCallback Action, CommandCancelAction OnCancel = nullptr); | |
static void SetReleaseFunction(TFunction<void (FActionQueue*)> Function); | |
static void SetCreateFunction(TFunction<FActionQueue* ()> Function); | |
static void SetGetFunction(TFunction<FActionQueue* ()> Function); | |
public: | |
//Tools | |
FActionQueue* Wait(float Seconds); | |
FActionQueue* WaitForNextTick(); | |
FActionQueue* AutoDestroy(); | |
void Clean(); | |
FActionQueue(); | |
~FActionQueue(); | |
private: | |
void WaitAndDo(CommandActionWithCallback Action); | |
void WaitAndDoNoCallback(CommandActionNoCallback Action); | |
void TryDequeue(); | |
private: | |
TQueue<FCommand> ActionQueueInternal; | |
CommandCancelAction CurrentCancelHandler; | |
FTimerHandle TimerHandle; | |
bool IsRunning; | |
#if WITH_EDITORONLY_DATA | |
FString DebugName; | |
#endif | |
static TFunction<void(FActionQueue*)> ReleaseFunction; | |
static TFunction<FActionQueue* ()> CreateFunction; | |
static TFunction<FActionQueue* ()> GetFunction; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment