Skip to content

Commit

Permalink
Added alternative version of TakeFromPool() that process multiple req…
Browse files Browse the repository at this point in the history
…uests at once, then fires event when all requested objects are ready
  • Loading branch information
JanSeliv committed Dec 28, 2023
1 parent c57a4bb commit 341c143
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 16 deletions.
Binary file modified Binaries/Win64/UnrealEditor-PoolManager.dll
Binary file not shown.
Binary file modified Binaries/Win64/UnrealEditor-PoolManager.pdb
Binary file not shown.
96 changes: 91 additions & 5 deletions Source/PoolManager/Private/PoolManagerSubsystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ UPoolManagerSubsystem* UPoolManagerSubsystem::GetPoolManagerByClass(TSubclassOf<
}

/*********************************************************************************************
* Main API
* Take From Pool
********************************************************************************************* */

// Async version of TakeFromPool() that returns the object by specified class
Expand Down Expand Up @@ -140,6 +140,79 @@ const FPoolObjectData* UPoolManagerSubsystem::TakeFromPoolOrNull(const UClass* O
return FoundData;
}

// Is alternative version of TakeFromPool() that process multiple requests at once, then fires event when all requested objects are ready
void UPoolManagerSubsystem::TakeFromPool(TArray<FSpawnRequest>& InOutRequests, const FOnSpawnAllCallback& Completed)
{
if (!ensureMsgf(!InOutRequests.IsEmpty(), TEXT("ASSERT: [%i] %s:\n'InOutRequests' is empty!"), __LINE__, *FString(__FUNCTION__)))
{
return;
}

TArray<FPoolObjectHandle> AllHandles;
TArray<FSpawnRequest> RequestsToCreate;

// --- Take if free objects in pool first
for (FSpawnRequest& It : InOutRequests)
{
if (const FPoolObjectData* ObjectData = TakeFromPoolOrNull(It.Class, It.Transform))
{
It.Handle = ObjectData->Handle;
}
else
{
It.Handle = FPoolObjectHandle::NewHandle(It.Class);
RequestsToCreate.Emplace(It);
}

AllHandles.Emplace(It.Handle);
}

if (RequestsToCreate.IsEmpty())
{
// All objects are taken from pool
if (Completed)
{
TArray<FPoolObjectData> OutObjects;
FindPoolObjectsByHandles(OutObjects, AllHandles);
Completed(OutObjects);
}
return;
}

// --- Process OnEachSpawned only if Completed is set
FOnSpawnCallback OnEachSpawned = nullptr;
if (Completed)
{
const TWeakObjectPtr<const ThisClass> WeakThis(this);
OnEachSpawned = [WeakThis, AllHandles, Completed](const FPoolObjectData& ObjectData)
{
const UPoolManagerSubsystem* PoolManager = WeakThis.Get();
if (!PoolManager)
{
return;
}

TArray<FPoolObjectData> OutObjects;
PoolManager->FindPoolObjectsByHandles(OutObjects, AllHandles);
if (OutObjects.Num() == AllHandles.Num())
{
Completed(OutObjects);
}
};
}

// --- Create the rest of objects
for (FSpawnRequest& It : RequestsToCreate)
{
It.Callbacks.OnPostSpawned = OnEachSpawned;
CreateNewObjectInPool(It);
}
}

/*********************************************************************************************
* Return To Pool
********************************************************************************************* */

// Returns the specified object to the pool and deactivates it if the object was taken from the pool before
bool UPoolManagerSubsystem::ReturnToPool_Implementation(UObject* Object)
{
Expand Down Expand Up @@ -203,7 +276,7 @@ bool UPoolManagerSubsystem::RegisterObjectInPool_Implementation(const FPoolObjec
if (!Data.Handle.IsValid())
{
// Hash can be unset that is fine, generate new one
Data.Handle = FPoolObjectHandle::NewHandle(*ObjectClass);
Data.Handle = FPoolObjectHandle::NewHandle(ObjectClass);
}

Pool.PoolObjects.Emplace(Data);
Expand All @@ -225,7 +298,7 @@ FPoolObjectHandle UPoolManagerSubsystem::CreateNewObjectInPool_Implementation(co
if (!Request.Handle.IsValid())
{
// Hash can be unset that is fine, generate new one
Request.Handle = FPoolObjectHandle::NewHandle(*Request.Class);
Request.Handle = FPoolObjectHandle::NewHandle(Request.Class);
}

// Always register new object in pool once it is spawned
Expand Down Expand Up @@ -510,11 +583,11 @@ int32 UPoolManagerSubsystem::GetRegisteredObjectsNum_Implementation(const UClass
}

// Returns the object associated with given handle
UObject* UPoolManagerSubsystem::FindPoolObjectByHandle(const FPoolObjectHandle& Handle) const
const FPoolObjectData& UPoolManagerSubsystem::FindPoolObjectByHandle(const FPoolObjectHandle& Handle) const
{
const FPoolContainer* Pool = FindPool(Handle.GetObjectClass());
const FPoolObjectData* ObjectData = Pool ? Pool->FindInPool(Handle) : nullptr;
return ObjectData ? ObjectData->PoolObject : nullptr;
return ObjectData ? *ObjectData : FPoolObjectData::EmptyObject;
}

// Returns handle associated with given object
Expand All @@ -525,6 +598,19 @@ const FPoolObjectHandle& UPoolManagerSubsystem::FindPoolHandleByObject(const UOb
return ObjectData ? ObjectData->Handle : FPoolObjectHandle::EmptyHandle;
}

// Returns from all given handles only valid ones
void UPoolManagerSubsystem::FindPoolObjectsByHandles(TArray<FPoolObjectData>& OutObjects, const TArray<FPoolObjectHandle>& InHandles) const
{
for (const FPoolObjectHandle& HandleIt : InHandles)
{
FPoolObjectData PoolObject = FindPoolObjectByHandle(HandleIt);
if (PoolObject.IsValid())
{
OutObjects.Emplace(MoveTemp(PoolObject));
}
}
}

/*********************************************************************************************
* Protected methods
********************************************************************************************* */
Expand Down
11 changes: 8 additions & 3 deletions Source/PoolManager/Private/PoolManagerTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ const FPoolObjectData FPoolObjectData::EmptyObject = FPoolObjectData();
const FPoolContainer FPoolContainer::EmptyPool = FPoolContainer();

// Generates a new handle for the specified object class
FPoolObjectHandle FPoolObjectHandle::NewHandle(const UClass& InObjectClass)
FPoolObjectHandle FPoolObjectHandle::NewHandle(const UClass* InObjectClass)
{
if (!ensureMsgf(InObjectClass, TEXT("ASSERT: [%i] %s:\n'InObjectClass' is null, can't generate new handle!"), __LINE__, *FString(__FUNCTION__)))
{
return EmptyHandle;
}

FPoolObjectHandle Handle;
Handle.ObjectClass = &InObjectClass;
Handle.ObjectClass = InObjectClass;
Handle.Hash = FGuid::NewGuid();
return Handle;
}
Expand Down Expand Up @@ -56,7 +61,7 @@ FPoolObjectData* FPoolContainer::FindInPool(const FPoolObjectHandle& Handle)
{
return nullptr;
}

return PoolObjects.FindByPredicate([&Handle](const FPoolObjectData& PoolObjectIt)
{
return PoolObjectIt.Handle == Handle;
Expand Down
29 changes: 22 additions & 7 deletions Source/PoolManager/Public/PoolManagerSubsystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,8 @@ class POOLMANAGER_API UPoolManagerSubsystem : public UWorldSubsystem
static UPoolManagerSubsystem* GetPoolManagerByClass(TSubclassOf<UPoolManagerSubsystem> OptionalClass = nullptr, const UObject* OptionalWorldContext = nullptr);

/*********************************************************************************************
* Main API
*
* Use TakeFromPool() to get it instead of creating by your own.
* Use ReturnToPool() to return it back to the pool instead of destroying by your own.
* Take From Pool
* Use it to get an object instead of creating it manually by your own.
********************************************************************************************* */
public:
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnTakenFromPool, UObject*, Object);
Expand All @@ -88,7 +86,7 @@ class POOLMANAGER_API UPoolManagerSubsystem : public UWorldSubsystem
UFUNCTION(BlueprintCallable, Category = "Pool Manager", meta = (BlueprintInternalUseOnly = "true"))
void BPTakeFromPool(const UClass* ObjectClass, const FTransform& Transform, const FOnTakenFromPool& Completed);

/** Is code-overridable version of TakeFromPool() that calls callback functions when the object is ready.
/** Is code-overridable alternative version of TakeFromPool() that calls callback functions when the object is ready.
* Can be overridden by child code classes.
* Is useful in code with blueprint classes, e.g: TakeFromPool(SomeBlueprintClass);
* @return Handle to the object with the Hash associated with the object, is indirect since the object could be not ready yet. */
Expand All @@ -102,6 +100,15 @@ class POOLMANAGER_API UPoolManagerSubsystem : public UWorldSubsystem
/** Is alternative version of TakeFromPool() to find object in pool or return null. */
virtual const FPoolObjectData* TakeFromPoolOrNull(const UClass* ObjectClass, const FTransform& Transform);

/** Is alternative version of TakeFromPool() that process multiple requests at once, then fires event when all requested objects are ready.
* @param InOutRequests Takes the classes and Transforms, returns the handles associated with objects to be spawned next frames.
* @param Completed The callback function that is called once when all objects are ready. */
virtual void TakeFromPool(TArray<FSpawnRequest>& InOutRequests, const FOnSpawnAllCallback& Completed = nullptr);

/*********************************************************************************************
* Return To Pool
* Returns an object back to the pool instead of destroying by your own.
********************************************************************************************* */
public:
/** Returns the specified object to the pool and deactivates it if the object was taken from the pool before.
* @param Object The object to return to the pool.
Expand Down Expand Up @@ -232,16 +239,24 @@ class POOLMANAGER_API UPoolManagerSubsystem : public UWorldSubsystem
int32 GetRegisteredObjectsNum(const UClass* ObjectClass) const;
virtual int32 GetRegisteredObjectsNum_Implementation(const UClass* ObjectClass) const;

/** Returns true if object is valid and registered in pool. */
UFUNCTION(BlueprintPure, Category = "Pool Manager", meta = (AutoCreateRefTerm = "InPoolObject"))
static bool IsPoolObjectValid(const FPoolObjectData& InPoolObject) { return InPoolObject.IsValid(); }

/** Returns the object associated with given handle.
* Can be null if not found or object is in spawning queue. */
* Can return invalid PoolObject if not found or object is in spawning queue. */
UFUNCTION(BlueprintPure, Category = "Pool Manager")
UObject* FindPoolObjectByHandle(const FPoolObjectHandle& Handle) const;
const FPoolObjectData& FindPoolObjectByHandle(const FPoolObjectHandle& Handle) const;

/** Returns handle associated with given object.
* Can be invalid (FPoolObjectHandle::EmptyHandle) if not found. */
UFUNCTION(BlueprintPure, Category = "Pool Manager", meta = (DefaultToSelf = "Object"))
const FPoolObjectHandle& FindPoolHandleByObject(const UObject* Object) const;

/** Returns from all given handles only valid ones. */
UFUNCTION(BlueprintPure, Category = "Pool Manager")
void FindPoolObjectsByHandles(TArray<FPoolObjectData>& OutObjects, const TArray<FPoolObjectHandle>& InHandles) const;

/*********************************************************************************************
* Protected properties
********************************************************************************************* */
Expand Down
3 changes: 2 additions & 1 deletion Source/PoolManager/Public/PoolManagerTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct POOLMANAGER_API FPoolObjectHandle
static const FPoolObjectHandle EmptyHandle;

/** Generates a new handle for the specified object class. */
static FPoolObjectHandle NewHandle(const UClass& InObjectClass);
static FPoolObjectHandle NewHandle(const UClass* InObjectClass);

/** Returns true if Hash is generated. */
FORCEINLINE bool IsValid() const { return ObjectClass && Hash.IsValid(); }
Expand Down Expand Up @@ -170,6 +170,7 @@ struct POOLMANAGER_API FPoolContainer
};

typedef TFunction<void(const FPoolObjectData&)> FOnSpawnCallback;
typedef TFunction<void(const TArray<FPoolObjectData>&)> FOnSpawnAllCallback;

/**
* Contains the functions that are called when the object is spawned.
Expand Down

0 comments on commit 341c143

Please sign in to comment.