Skip to content

Instantly share code, notes, and snippets.

@vgvgvvv
Created December 2, 2021 15:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vgvgvvv/63339908137485cbd640c8f77d2a4eb2 to your computer and use it in GitHub Desktop.
Save vgvgvvv/63339908137485cbd640c8f77d2a4eb2 to your computer and use it in GitHub Desktop.
ActionQueue For Unreal Engine
#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;
}
}
#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