Skip to content

Instantly share code, notes, and snippets.

@vgvgvvv
Created December 2, 2021 15:20
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/6b539beeca3c1dba2fe65633fe196289 to your computer and use it in GitHub Desktop.
Save vgvgvvv/6b539beeca3c1dba2fe65633fe196289 to your computer and use it in GitHub Desktop.
#include "ActionConsumeTickManager.h"
#include "DynamicStats.h"
FConsumable* FConsumable::AddAction(TFunction<void()> InFunc)
{
Parent->AddAction(InFunc);
return this;
}
void FConsumable::ConsumeAction(float DeltaTime)
{
Parent->ConsumeAction(DeltaTime);
}
FBaseConsumeQueue* FConsumable::GetRoot()
{
FConsumable* Root = this;
while (Root->Parent != nullptr)
{
Root = Root->Parent;
}
return static_cast<FBaseConsumeQueue*>(Root);
}
bool FConsumable::IsEmpty()
{
return GetRoot()->IsQueueEmpty();
}
void FConsumable::Clear()
{
GetRoot()->ClearQueue();
}
void FConsumable::Flush()
{
auto Root = GetRoot();
IsFlushing = true;
while (!Root->IsQueueEmpty())
{
ConsumeAction(0);
}
IsFlushing = false;
}
FConsumable* FConsumable::AddInterval(float Invertal)
{
return new FDelayTimeConsumeQueue(this, Invertal);
}
FConsumable* FConsumable::SetConsumeNumber(int Count)
{
return new FCountConsumeQueue(this, Count);
}
FConsumable* FConsumable::SetConsumeCondition(TFunction<bool(FConsumable*)> InAddCondition,
TFunction<bool(FConsumable*)> InConsumeCondition)
{
return new FConditionConsumeQueue(this, InAddCondition, InConsumeCondition);
}
FConsumable* FConsumable::NotDelayUntilCallCount(int CallCount)
{
return new FNotDelayUntilCallCountConsumeQueue(this, CallCount);
}
FConsumable* FConsumable::SetTimeLimit(int Time)
{
return new FTimelimitConsueQueue(this, Time);
}
FConsumable* FConsumable::SetNext(FConsumable* Next)
{
if(!Next)
{
return this;
}
return new FOnNextConsumeQueue(this, Next);
}
FConsumable* FConsumable::Create()
{
return new FBaseConsumeQueue();
}
void FConsumable::SetOwner(FActionConsumeTickManager* InOwner)
{
Owner = InOwner;
}
void FBaseConsumeQueue::ConsumeAction(float DeltaTime)
{
if (!ConsumeQueue.IsEmpty())
{
FConsumableAction Action;
ConsumeQueue.Dequeue(Action);
Action.Execute();
}
}
FConsumable* FBaseConsumeQueue::AddAction(TFunction<void()> InFunc)
{
ConsumeQueue.Enqueue(FConsumableAction(InFunc));
return this;
}
bool FBaseConsumeQueue::IsQueueEmpty()
{
return ConsumeQueue.IsEmpty();
}
void FBaseConsumeQueue::ClearQueue()
{
ConsumeQueue.Empty();
}
void FDelayTimeConsumeQueue::ConsumeAction(float DeltaTime)
{
if(IsFlushing)
{
Clear();
return;
}
if (IsEmpty())
{
return;
}
if (CurrentTime < WaitTime)
{
CurrentTime += DeltaTime;
return;
}
Parent->ConsumeAction(CurrentTime);
CurrentTime = 0;
}
void FCountConsumeQueue::ConsumeAction(float DeltaTime)
{
for (int i = 0; i < ConsumCountOneFrame; i++)
{
if (IsEmpty())
{
return;
}
if (i > 0)
{
// 中间的耗时应为0
Parent->ConsumeAction(0);
}
else
{
Parent->ConsumeAction(DeltaTime);
}
}
}
FConsumable* FConditionConsumeQueue::AddAction(TFunction<void()> InFunc)
{
if(IsFlushing)
{
return this;
}
if (AddCondition(this))
{
Parent->AddAction(InFunc);
}
return this;
}
void FConditionConsumeQueue::ConsumeAction(float DeltaTime)
{
if (IsFlushing)
{
// 防止Flash时反复重试导致死循环
Clear();
return;
}
if (IsEmpty())
{
return;
}
if (ConsumeCondition(this))
{
Parent->ConsumeAction(DeltaTime);
}
}
void FNotDelayUntilCallCountConsumeQueue::ConsumeAction(float DeltaTime)
{
if(!IsEmpty())
{
Parent->ConsumeAction(DeltaTime);
}
CurrentCount = 0;
}
FConsumable* FNotDelayUntilCallCountConsumeQueue::AddAction(TFunction<void()> InFunc)
{
if(CurrentCount < CallCount)
{
InFunc();
CurrentCount++;
}else
{
Parent->AddAction(InFunc);
}
return this;
}
void FTimelimitConsueQueue::ConsumeAction(float DeltaTime)
{
if (IsFlushing)
{
// 防止Flash时反复重试导致死循环
Clear();
return;
}
auto BeginTime = FPlatformTime::Cycles64();
bool IsFirst = true;
while (!IsEmpty() && (FPlatformTime::Cycles64() - BeginTime) * FPlatformTime::GetSecondsPerCycle64() * 1000 < ConsumeLimitTime)
{
if(IsFirst)
{
Parent->ConsumeAction(DeltaTime);
IsFirst = false;
}else
{
Parent->ConsumeAction(0);
}
}
}
void FOnNextConsumeQueue::ConsumeAction(float DeltaTime)
{
if (IsFlushing)
{
// 防止Flash时反复重试导致死循环
Clear();
return;
}
Parent->ConsumeAction(DeltaTime);
if(IsEmpty())
{
Owner->AddConsumeQueue(NextConsumable);
}
}
FActionConsumeTickManager::~FActionConsumeTickManager()
{
static TArray<FString> Keys;
Keys.Empty();
ConsumeActionQueueMap.GetKeys(Keys);
for (int i = 0; i < Keys.Num(); i++)
{
auto Temp = ConsumeActionQueueMap[Keys[i]];
Temp->Clear();
ConsumeActionQueueMap.Remove(Keys[i]);
delete Temp;
}
for (int i = TempConsumables.Num() - 1; i >= 0; i--)
{
auto Consumable = TempConsumables[i];
Consumable->Clear();
delete Consumable;
}
TempConsumables.Empty();
}
void FActionConsumeTickManager::Tick(float DeltaTime)
{
if(bSkipThisFrame)
{
bSkipThisFrame = false;
return;
}
QUICK_SCOPE_CYCLE_COUNTER(Stat_FActionConsumeTickManager_Tick)
static TArray<FString> Keys;
Keys.Empty();
ConsumeActionQueueMap.GetKeys(Keys);
for (int i = 0; i < Keys.Num(); i++)
{
DYNAMIC_SCOPE_CYCLE_COUNTER(Stat_FActionConsumeTickManager_Tick, Keys[i])
auto Consumable = ConsumeActionQueueMap[Keys[i]];
Consumable->ConsumeAction(DeltaTime);
if(Consumable->IsEmpty() && Consumable->AutoRemove)
{
ConsumeActionQueueMap.Remove(Keys[i]);
delete Consumable;
}
}
static TArray<FConsumable*> Remove;
Remove.Empty();
for (int i = TempConsumables.Num() - 1; i >= 0; i--)
{
QUICK_SCOPE_CYCLE_COUNTER(Stat_FActionConsumeTickManager_Temp_Tick)
auto Consumable = TempConsumables[i];
Consumable->ConsumeAction(DeltaTime);
if (Consumable->IsEmpty())
{
Remove.Add(TempConsumables[i]);
}
}
for(int i = 0; i < Remove.Num(); i ++)
{
auto Consumable = Remove[i];
TempConsumables.Remove(Consumable);
delete Consumable;
}
}
void FActionConsumeTickManager::AddConsumeQueue(FString QueueName, FConsumable* Consumable)
{
if (!ConsumeActionQueueMap.Contains(QueueName) && Consumable)
{
Consumable->SetOwner(this);
ConsumeActionQueueMap.Add(QueueName, Consumable);
}
}
void FActionConsumeTickManager::AddConsumeQueue(FConsumable* Consumable)
{
if(!Consumable)
{
return;
}
Consumable->SetOwner(this);
TempConsumables.Add(Consumable);
}
void FActionConsumeTickManager::Flush()
{
static TArray<FString> Keys;
Keys.Empty();
ConsumeActionQueueMap.GetKeys(Keys);
for (int i = 0; i < Keys.Num(); i++)
{
ConsumeActionQueueMap[Keys[i]]->Flush();
}
for(int i = TempConsumables.Num() - 1; i >=0 ; i--)
{
auto Consumable = TempConsumables[i];
Consumable->Flush();
delete Consumable;
}
TempConsumables.Empty();
}
void FActionConsumeTickManager::Clear()
{
static TArray<FString> Keys;
Keys.Empty();
ConsumeActionQueueMap.GetKeys(Keys);
for (int i = 0; i < Keys.Num(); i++)
{
ConsumeActionQueueMap[Keys[i]]->Clear();
}
for(int i = TempConsumables.Num() - 1; i >=0 ; i--)
{
auto Consumable = TempConsumables[i];
Consumable->Clear();
delete Consumable;
}
TempConsumables.Empty();
}
void FActionConsumeTickManager::SkipThisFrame()
{
bSkipThisFrame = true;
}
FConsumable* FActionConsumeTickManager::GetConsumable(FString QueueName)
{
if (!ConsumeActionQueueMap.Contains(QueueName))
{
return nullptr;
}
return ConsumeActionQueueMap[QueueName];
}
bool FActionConsumeTickManager::GetOrAddConsumable(FString QueueName, FConsumable*& Result)
{
if (!ConsumeActionQueueMap.Contains(QueueName))
{
Result = FConsumable::Create();
ConsumeActionQueueMap.Add(QueueName, Result);
return true;
}
Result = ConsumeActionQueueMap[QueueName];
return false;
}
bool FActionConsumeTickManager::GetOrAddConsumable(FString QueueName, FConsumable*& Result, TFunction<FConsumable*(FConsumable*)> OnCreate)
{
if (!ConsumeActionQueueMap.Contains(QueueName))
{
Result = OnCreate(FConsumable::Create());
ConsumeActionQueueMap.Add(QueueName, Result);
return true;
}
Result = ConsumeActionQueueMap[QueueName];
return false;
}
bool FActionConsumeTickManager::IsTickable() const
{
return true;
}
TStatId FActionConsumeTickManager::GetStatId() const
{
#if UE_BUILD_SHIPPING
return TStatId();
#else
FString Temp = TEXT("FActionConsumeTickManager");
return FDynamicStats::CreateStatId<FStatGroup_STATGROUP_UObjects>(Temp);
#endif
}
#pragma once
#include "Queue.h"
#include "Tickable.h"
#include "Function.h"
class FActionConsumeTickManager;
class ENGINE_API FConsumableAction
{
public:
FConsumableAction() { }
FConsumableAction(TFunction<void()> InFunc) { Func = InFunc; }
void Execute()
{
Func();
};
private:
TFunction<void()> Func;
};
// 可消耗Tick的基类,用于后续包装
class ENGINE_API FConsumable
{
public:
FConsumable() :
FConsumable(nullptr) {}
FConsumable(FConsumable* Consumable) :
Owner(nullptr), Parent(Consumable), IsFlushing(false), AutoRemove(false) {}
virtual ~FConsumable()
{
if (Parent != nullptr)
{
delete Parent;
}
}
/**
* 添加消耗的行为
*/
virtual FConsumable* AddAction(TFunction<void()> InFunc);;
/**
* 一帧内的消耗
*/
virtual void ConsumeAction(float DeltaTime);
class FConsumable* AddInterval(float Invertal);
class FBaseConsumeQueue* GetRoot();
bool IsEmpty();
void Clear();
void Flush();
class FConsumable* SetConsumeNumber(int Count);
class FConsumable* SetConsumeCondition(TFunction<bool(FConsumable*)> InAddCondition, TFunction<bool(FConsumable*)> InConsumeCondition);
// 在一定数量下直接调用,如果超过则加入消耗队列
class FConsumable* NotDelayUntilCallCount(int CallCount);
// 在一定时间内消耗行为,超过时间则停止
class FConsumable* SetTimeLimit(int Time);
class FConsumable* SetNext(FConsumable* Next);
static class FConsumable* Create();
void SetOwner(FActionConsumeTickManager* InOwner);
public:
FActionConsumeTickManager* Owner;
FConsumable* Parent;
bool IsFlushing;
bool AutoRemove;
};
class FBaseConsumeQueue : public FConsumable
{
public:
FBaseConsumeQueue() {}
void ConsumeAction(float DeltaTime) override;
FConsumable* AddAction(TFunction<void()> InFunc) override;
bool IsQueueEmpty();
void ClearQueue();
private:
TQueue<FConsumableAction> ConsumeQueue;
};
class ENGINE_API FDelayTimeConsumeQueue : public FConsumable
{
public:
FDelayTimeConsumeQueue(FConsumable* Consumable, float InWaitTime) :
FConsumable(Consumable)
, WaitTime(InWaitTime)
, CurrentTime(0)
{
}
void ConsumeAction(float DeltaTime) override;;
private:
float WaitTime;
float CurrentTime;
};
class ENGINE_API FCountConsumeQueue : public FConsumable
{
public:
FCountConsumeQueue(FConsumable* Consumable, int InConsumCountOneFrame)
: FConsumable(Consumable), ConsumCountOneFrame(InConsumCountOneFrame)
{
}
void ConsumeAction(float DeltaTime) override;
private:
int ConsumCountOneFrame;
};
class ENGINE_API FConditionConsumeQueue : public FConsumable
{
public:
FConditionConsumeQueue(FConsumable* Consumable, TFunction<bool(FConsumable*)> InAddCondition, TFunction<bool(FConsumable*)> InConsumeCondition)
: FConsumable(Consumable), AddCondition(InAddCondition), ConsumeCondition(InConsumeCondition)
{
}
FConsumable* AddAction(TFunction<void()> InFunc) override;
void ConsumeAction(float DeltaTime) override;
private:
TFunction<bool(FConsumable*)> AddCondition;
TFunction<bool(FConsumable*)> ConsumeCondition;
};
// 直接进行调用直到一帧内调用次数超过一定数量
class ENGINE_API FNotDelayUntilCallCountConsumeQueue : public FConsumable
{
public:
FNotDelayUntilCallCountConsumeQueue(FConsumable* Consumable, int InCallCount)
: FConsumable(Consumable), CallCount(InCallCount),CurrentCount(0)
{
}
void ConsumeAction(float DeltaTime) override;
FConsumable* AddAction(TFunction<void()> InFunc) override;
private:
int CallCount;
int CurrentCount;
};
class ENGINE_API FTimelimitConsueQueue : public FConsumable
{
public:
FTimelimitConsueQueue(FConsumable* Consumable, int InConsumeLimitTime)
: FConsumable(Consumable), ConsumeLimitTime(InConsumeLimitTime)
{
}
void ConsumeAction(float DeltaTime) override;
private:
//限制时间单位:ms
int ConsumeLimitTime;
};
class ENGINE_API FOnNextConsumeQueue : public FConsumable
{
public:
FOnNextConsumeQueue(FConsumable* Consumable, FConsumable* InNextConsumable)
: FConsumable(Consumable), NextConsumable(InNextConsumable)
{
}
void ConsumeAction(float DeltaTime) override;
private:
FConsumable* NextConsumable;
};
class ENGINE_API FActionConsumeTickManager : public FTickableGameObject
{
public:
FActionConsumeTickManager()
: bSkipThisFrame(false)
{
}
virtual ~FActionConsumeTickManager() override;
void Tick(float DeltaTime) override;
bool IsTickable() const override;
TStatId GetStatId() const override;
void AddConsumeQueue(FString QueueName, FConsumable* Consumable);
void AddConsumeQueue(FConsumable* Consumable);
FConsumable* GetConsumable(FString QueueName);
bool GetOrAddConsumable(FString QueueName, FConsumable*& Result);
bool GetOrAddConsumable(FString QueueName, FConsumable*& Result, TFunction<FConsumable*(FConsumable*)> OnCreate);
void Flush();
void Clear();
void SkipThisFrame();
private:
TMap<FString, FConsumable*> ConsumeActionQueueMap;
TArray<FConsumable*> TempConsumables;
bool bSkipThisFrame;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment