diff --git a/Source/PoolManager/Private/Factories/PoolFactory_Actor.cpp b/Source/PoolManager/Private/Factories/PoolFactory_Actor.cpp index 32c013c..0c8c021 100644 --- a/Source/PoolManager/Private/Factories/PoolFactory_Actor.cpp +++ b/Source/PoolManager/Private/Factories/PoolFactory_Actor.cpp @@ -39,9 +39,14 @@ UObject* UPoolFactory_Actor::SpawnNow_Implementation(const FSpawnRequest& Reques AActor* NewActor = World->SpawnActor(ClassToSpawn, &Request.Transform, SpawnParameters); checkf(NewActor, TEXT("ERROR: [%i] %s:\n'NewActor' was not spawned!"), __LINE__, *FString(__FUNCTION__)); - if (Request.Callbacks.OnPreConstructed != nullptr) + FPoolObjectData PoolObjectData; + PoolObjectData.bIsActive = true; + PoolObjectData.PoolObject = NewActor; + PoolObjectData.Handle = Request.Handle; + + if (Request.Callbacks.OnPreRegistered != nullptr) { - Request.Callbacks.OnPreConstructed(NewActor); + Request.Callbacks.OnPreRegistered(PoolObjectData); } if (AActor* SpawnedActor = Cast(NewActor)) @@ -52,7 +57,7 @@ UObject* UPoolFactory_Actor::SpawnNow_Implementation(const FSpawnRequest& Reques if (Request.Callbacks.OnPostSpawned != nullptr) { - Request.Callbacks.OnPostSpawned(NewActor); + Request.Callbacks.OnPostSpawned(PoolObjectData); } return NewActor; diff --git a/Source/PoolManager/Private/Factories/PoolFactory_UObject.cpp b/Source/PoolManager/Private/Factories/PoolFactory_UObject.cpp index 94d4c33..85e7ff8 100644 --- a/Source/PoolManager/Private/Factories/PoolFactory_UObject.cpp +++ b/Source/PoolManager/Private/Factories/PoolFactory_UObject.cpp @@ -9,13 +9,18 @@ // Method to queue object spawn requests void UPoolFactory_UObject::RequestSpawn_Implementation(const FSpawnRequest& Request) { + if (!ensureMsgf(Request.IsValid(), TEXT("ASSERT: [%i] %s:\n'Request' is not valid and can't be processed!"), __LINE__, *FString(__FUNCTION__))) + { + return; + } + // Add request to queue - SpawnQueueInternal.Enqueue(Request); + SpawnQueueInternal.Emplace(Request); // If this is the first object in the queue, schedule the OnNextTickProcessSpawn to be called on the next frame // Creating UObjects on separate threads is not thread-safe and leads to problems with garbage collection, // so we will create them on the game thread, but defer to next frame to avoid hitches - if (++SpawnQueueSize == 1) + if (SpawnQueueInternal.Num() == 1) { const UWorld* World = GetWorld(); checkf(World, TEXT("ERROR: [%i] %s:\n'World' is null!"), __LINE__, *FString(__FUNCTION__)); @@ -29,14 +34,19 @@ UObject* UPoolFactory_UObject::SpawnNow_Implementation(const FSpawnRequest& Requ { UObject* CreatedObject = NewObject(GetOuter(), Request.Class); - if (Request.Callbacks.OnPreConstructed != nullptr) + FPoolObjectData PoolObjectData; + PoolObjectData.bIsActive = true; + PoolObjectData.PoolObject = CreatedObject; + PoolObjectData.Handle = Request.Handle; + + if (Request.Callbacks.OnPreRegistered != nullptr) { - Request.Callbacks.OnPreConstructed(CreatedObject); + Request.Callbacks.OnPreRegistered(PoolObjectData); } if (Request.Callbacks.OnPostSpawned != nullptr) { - Request.Callbacks.OnPostSpawned(CreatedObject); + Request.Callbacks.OnPostSpawned(PoolObjectData); } return CreatedObject; @@ -45,8 +55,36 @@ UObject* UPoolFactory_UObject::SpawnNow_Implementation(const FSpawnRequest& Requ // Removes the first spawn request from the queue and returns it bool UPoolFactory_UObject::DequeueSpawnRequest(FSpawnRequest& OutRequest) { - --SpawnQueueSize; - return SpawnQueueInternal.Dequeue(OutRequest); + if (!SpawnQueueInternal.IsValidIndex(0)) + { + return false; + } + + // Copy and remove first request from the queue (without Swap to keep order) + OutRequest = SpawnQueueInternal[0]; + SpawnQueueInternal.RemoveAt(0); + + return OutRequest.IsValid(); +} + +// Alternative method to remove specific spawn request from the queue and returns it. +bool UPoolFactory_UObject::DequeueSpawnRequestByHandle(const FPoolObjectHandle& Handle, FSpawnRequest& OutRequest) +{ + const int32 Idx = SpawnQueueInternal.IndexOfByPredicate([&Handle](const FSpawnRequest& Request) + { + return Request.Handle == Handle; + }); + + if (!SpawnQueueInternal.IsValidIndex(Idx)) + { + return false; + } + + // Copy and remove first request from the queue (without Swap to keep order) + OutRequest = SpawnQueueInternal[Idx]; + SpawnQueueInternal.RemoveAt(Idx); + + return OutRequest.IsValid(); } // Is called on next frame to process a chunk of the spawn queue @@ -58,7 +96,7 @@ void UPoolFactory_UObject::OnNextTickProcessSpawn_Implementation() ObjectsPerFrame = 1; } - for (int32 Index = 0; Index < FMath::Min(ObjectsPerFrame, SpawnQueueSize); ++Index) + for (int32 Index = 0; Index < FMath::Min(ObjectsPerFrame, SpawnQueueInternal.Num()); ++Index) { FSpawnRequest OutRequest; if (DequeueSpawnRequest(OutRequest)) diff --git a/Source/PoolManager/Private/PoolManagerSubsystem.cpp b/Source/PoolManager/Private/PoolManagerSubsystem.cpp index 33a1f01..4fe2654 100644 --- a/Source/PoolManager/Private/PoolManagerSubsystem.cpp +++ b/Source/PoolManager/Private/PoolManagerSubsystem.cpp @@ -58,46 +58,47 @@ UPoolManagerSubsystem* UPoolManagerSubsystem::GetPoolManagerByClass(TSubclassOf< // Async version of TakeFromPool() that returns the object by specified class void UPoolManagerSubsystem::BPTakeFromPool(const UClass* ObjectClass, const FTransform& Transform, const FOnTakenFromPool& Completed) { - UObject* PoolObject = TakeFromPoolOrNull(ObjectClass, Transform); - if (PoolObject) + const FPoolObjectData* ObjectData = TakeFromPoolOrNull(ObjectClass, Transform); + if (ObjectData) { // Found in pool - Completed.ExecuteIfBound(PoolObject); + Completed.ExecuteIfBound(ObjectData->PoolObject); return; } FSpawnRequest Request; Request.Class = ObjectClass; Request.Transform = Transform; - Request.Callbacks.OnPostSpawned = [Completed](UObject* SpawnedObject) + Request.Callbacks.OnPostSpawned = [Completed](const FPoolObjectData& ObjectData) { - Completed.ExecuteIfBound(SpawnedObject); + Completed.ExecuteIfBound(ObjectData.PoolObject); }; CreateNewObjectInPool(Request); } // Is code async version of TakeFromPool() that calls callback functions when the object is ready -void UPoolManagerSubsystem::TakeFromPool(const UClass* ObjectClass, const FTransform& Transform/* = FTransform::Identity*/, const TFunction& Completed/* = nullptr*/) +FPoolObjectHandle UPoolManagerSubsystem::TakeFromPool(const UClass* ObjectClass, const FTransform& Transform/* = FTransform::Identity*/, const FOnSpawnCallback& Completed/* = nullptr*/) { - UObject* PoolObject = TakeFromPoolOrNull(ObjectClass, Transform); - if (PoolObject) + const FPoolObjectData* ObjectData = TakeFromPoolOrNull(ObjectClass, Transform); + if (ObjectData) { if (Completed != nullptr) { - Completed(PoolObject); + Completed(*ObjectData); } - return; + + return ObjectData->Handle; } FSpawnRequest Request; Request.Class = ObjectClass; Request.Transform = Transform; Request.Callbacks.OnPostSpawned = Completed; - CreateNewObjectInPool(Request); + return CreateNewObjectInPool(Request); } // Is internal function to find object in pool or return null -UObject* UPoolManagerSubsystem::TakeFromPoolOrNull_Implementation(const UClass* ObjectClass, const FTransform& Transform) +const FPoolObjectData* UPoolManagerSubsystem::TakeFromPoolOrNull(const UClass* ObjectClass, const FTransform& Transform) { if (!ensureMsgf(ObjectClass, TEXT("%s: 'ObjectClass' is not specified"), *FString(__FUNCTION__))) { @@ -112,36 +113,38 @@ UObject* UPoolManagerSubsystem::TakeFromPoolOrNull_Implementation(const UClass* } // Try to find first object contained in the Pool by its class that is inactive and ready to be taken from pool - UObject* FoundObject = nullptr; - for (const FPoolObjectData& PoolObjectIt : Pool->PoolObjects) + const FPoolObjectData* FoundData = nullptr; + for (const FPoolObjectData& DataIt : Pool->PoolObjects) { - if (PoolObjectIt.IsFree()) + if (DataIt.IsFree()) { - FoundObject = PoolObjectIt.Get(); + FoundData = &DataIt; break; } } - if (!FoundObject) + if (!FoundData) { // No free objects in pool return nullptr; } - Pool->GetFactoryChecked().OnTakeFromPool(FoundObject, Transform); + UObject& InObject = FoundData->GetChecked(); + + Pool->GetFactoryChecked().OnTakeFromPool(&InObject, Transform); - SetObjectStateInPool(EPoolObjectState::Active, *FoundObject, *Pool); + SetObjectStateInPool(EPoolObjectState::Active, InObject, *Pool); - return FoundObject; + return FoundData; } // Returns the specified object to the pool and deactivates it if the object was taken from the pool before -void UPoolManagerSubsystem::ReturnToPool_Implementation(UObject* Object) +bool UPoolManagerSubsystem::ReturnToPool_Implementation(UObject* Object) { FPoolContainer* Pool = Object ? FindPool(Object->GetClass()) : nullptr; if (!ensureMsgf(Pool, TEXT("ASSERT: [%i] %s:\n'Pool' is not not registered for '%s' object, can not return it to pool!"), __LINE__, *FString(__FUNCTION__), *GetNameSafe(Object))) { - return; + return false; } UPoolFactory_UObject& Factory = Pool->GetFactoryChecked(); @@ -157,6 +160,30 @@ void UPoolManagerSubsystem::ReturnToPool_Implementation(UObject* Object) Factory.DequeueSpawnRequest(OutRequest); TakeFromPool(OutRequest.Class, OutRequest.Transform, OutRequest.Callbacks.OnPostSpawned); } + + return true; +} + +// Alternative to ReturnToPool() to return object to the pool by its handle +bool UPoolManagerSubsystem::ReturnToPool(const FPoolObjectHandle& Handle) +{ + if (UObject* PoolObject = FindPoolObjectByHandle(Handle)) + { + return ReturnToPool(PoolObject); + } + + // Object is not found, attempt to cancel spawn request if is in queue + for (const TTuple, TObjectPtr>& It : AllFactoriesInternal) + { + FSpawnRequest OutRequest; + if (It.Value && It.Value->DequeueSpawnRequestByHandle(Handle, OutRequest)) + { + // Spawn request is found and removed from the queue + return true; + } + } + + return false; } /********************************************************************************************* @@ -164,47 +191,65 @@ void UPoolManagerSubsystem::ReturnToPool_Implementation(UObject* Object) ********************************************************************************************* */ // Adds specified object as is to the pool by its class to be handled by the Pool Manager -bool UPoolManagerSubsystem::RegisterObjectInPool_Implementation(UObject* Object, EPoolObjectState PoolObjectState/* = EPoolObjectState::Inactive*/) +bool UPoolManagerSubsystem::RegisterObjectInPool_Implementation(const FPoolObjectData& InData) { - if (!ensureMsgf(Object, TEXT("ASSERT: [%i] %s:\n'Object' is null, can't register!"), __LINE__, *FString(__FUNCTION__))) + if (!ensureMsgf(InData.PoolObject, TEXT("ASSERT: [%i] %s:\n'PoolObject' is not valid, can't registed it in the Pool!"), __LINE__, *FString(__FUNCTION__))) { return false; } - const UClass* ObjectClass = Object->GetClass(); + const UClass* ObjectClass = InData.PoolObject.GetClass(); FPoolContainer& Pool = FindPoolOrAdd(ObjectClass); - if (Pool.FindInPool(*Object)) + if (Pool.FindInPool(*InData.PoolObject)) { // Already contains in pool return false; } - FPoolObjectData& ObjectDataRef = Pool.PoolObjects.Emplace_GetRef(Object); - ObjectDataRef.bIsActive = PoolObjectState == EPoolObjectState::Active; + FPoolObjectData Data = InData; + if (!Data.Handle.IsValid()) + { + // Hash can be unset that is fine, generate new one + Data.Handle = FPoolObjectHandle::NewHandle(*ObjectClass); + } - SetObjectStateInPool(PoolObjectState, *Object, Pool); + Pool.PoolObjects.Emplace(Data); + + SetObjectStateInPool(Data.GetState(), *Data.PoolObject, Pool); return true; } // Always creates new object and adds it to the pool by its class -void UPoolManagerSubsystem::CreateNewObjectInPool_Implementation(const FSpawnRequest& InRequest) +FPoolObjectHandle UPoolManagerSubsystem::CreateNewObjectInPool_Implementation(const FSpawnRequest& InRequest) { + if (!ensureMsgf(InRequest.Class, TEXT("ASSERT: [%i] %s:\n'Class' is not null in the Spawn Request!"), __LINE__, *FString(__FUNCTION__))) + { + return FPoolObjectHandle::EmptyHandle; + } + FSpawnRequest Request = InRequest; + if (!Request.Handle.IsValid()) + { + // Hash can be unset that is fine, generate new one + Request.Handle = FPoolObjectHandle::NewHandle(*Request.Class); + } // Always register new object in pool once it is spawned const TWeakObjectPtr WeakThis(this); - Request.Callbacks.OnPreConstructed = [WeakThis](UObject* Object) + Request.Callbacks.OnPreRegistered = [WeakThis](const FPoolObjectData& ObjectData) { if (UPoolManagerSubsystem* PoolManager = WeakThis.Get()) { - PoolManager->RegisterObjectInPool(Object, EPoolObjectState::Active); + PoolManager->RegisterObjectInPool(ObjectData); } }; const FPoolContainer& Pool = FindPoolOrAdd(Request.Class); Pool.GetFactoryChecked().RequestSpawn(Request); + + return Request.Handle; } /********************************************************************************************* @@ -408,7 +453,7 @@ EPoolObjectState UPoolManagerSubsystem::GetPoolObjectState_Implementation(const return EPoolObjectState::None; } - return PoolObject->IsActive() ? EPoolObjectState::Active : EPoolObjectState::Inactive; + return PoolObject->GetState(); } // Returns true is specified object is handled by Pool Manager @@ -481,6 +526,30 @@ int32 UPoolManagerSubsystem::GetRegisteredObjectsNum_Implementation(const UClass return RegisteredObjectsNum; } +// Returns the object associated with given handle +UObject* UPoolManagerSubsystem::FindPoolObjectByHandle(const FPoolObjectHandle& Handle) const +{ + if (!Handle.IsValid()) + { + // Handle is empty, nothing to find + return nullptr; + } + + const FPoolContainer* Pool = FindPool(Handle.GetObjectClass()); + if (!Pool) + { + // Pool is not registered + return nullptr; + } + + const FPoolObjectData* FoundData = Pool->PoolObjects.FindByPredicate([&Handle](const FPoolObjectData& PoolObjectIt) + { + return PoolObjectIt.Handle == Handle; + }); + + return FoundData ? FoundData->PoolObject : nullptr; +} + /********************************************************************************************* * Protected methods ********************************************************************************************* */ diff --git a/Source/PoolManager/Private/PoolManagerTypes.cpp b/Source/PoolManager/Private/PoolManagerTypes.cpp index 230da6e..ddcdd23 100644 --- a/Source/PoolManager/Private/PoolManagerTypes.cpp +++ b/Source/PoolManager/Private/PoolManagerTypes.cpp @@ -4,18 +4,36 @@ //--- #include UE_INLINE_GENERATED_CPP_BY_NAME(PoolManagerTypes) +// Empty pool object handle +const FPoolObjectHandle FPoolObjectHandle::EmptyHandle = FPoolObjectHandle(); + // Empty pool object data const FPoolObjectData FPoolObjectData::EmptyObject = FPoolObjectData(); // Empty pool data container const FPoolContainer FPoolContainer::EmptyPool = FPoolContainer(); +// Generates a new handle for the specified object class +FPoolObjectHandle FPoolObjectHandle::NewHandle(const UClass& InObjectClass) +{ + FPoolObjectHandle Handle; + Handle.ObjectClass = &InObjectClass; + Handle.Hash = FGuid::NewGuid(); + return Handle; +} + // Parameterized constructor that takes object to keep FPoolObjectData::FPoolObjectData(UObject* InPoolObject) { PoolObject = InPoolObject; } +// Returns the state of the object in the pool +EPoolObjectState FPoolObjectData::GetState() const +{ + return bIsActive ? EPoolObjectState::Active : EPoolObjectState::Inactive; +} + // Parameterized constructor that takes a class of the pool FPoolContainer::FPoolContainer(const UClass* InClass) { diff --git a/Source/PoolManager/Public/Factories/PoolFactory_UObject.h b/Source/PoolManager/Public/Factories/PoolFactory_UObject.h index 5edd826..4d29c83 100644 --- a/Source/PoolManager/Public/Factories/PoolFactory_UObject.h +++ b/Source/PoolManager/Public/Factories/PoolFactory_UObject.h @@ -49,6 +49,10 @@ class POOLMANAGER_API UPoolFactory_UObject : public UObject UFUNCTION(BlueprintCallable, Category = "Pool Factory") virtual bool DequeueSpawnRequest(FSpawnRequest& OutRequest); + /** Alternative method to remove specific spawn request from the queue and returns it. */ + UFUNCTION(BlueprintCallable, Category = "Pool Factory") + virtual bool DequeueSpawnRequestByHandle(const FPoolObjectHandle& Handle, FSpawnRequest& OutRequest); + /** Returns true if the spawn queue is empty, so there are no spawn request at current moment. */ UFUNCTION(BlueprintPure, Category = "Pool Factory") virtual FORCEINLINE bool IsSpawnQueueEmpty() const { return SpawnQueueInternal.IsEmpty(); } @@ -91,10 +95,7 @@ class POOLMANAGER_API UPoolFactory_UObject : public UObject * Data ********************************************************************************************* */ protected: - /** Request to spawn. */ - TQueue SpawnQueueInternal; - - /** Number of spawn requests in the queue. */ - UPROPERTY(Transient) - int32 SpawnQueueSize = 0; + /** All request to spawn. */ + UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Transient, Category = "Pool Factory", meta = (BlueprintProtected, DisplayName = "Spawn Queue")) + TArray SpawnQueueInternal; }; diff --git a/Source/PoolManager/Public/PoolManagerSubsystem.h b/Source/PoolManager/Public/PoolManagerSubsystem.h index d788093..837871f 100644 --- a/Source/PoolManager/Public/PoolManagerSubsystem.h +++ b/Source/PoolManager/Public/PoolManagerSubsystem.h @@ -90,23 +90,31 @@ class POOLMANAGER_API UPoolManagerSubsystem : public UWorldSubsystem /** Is code-overridable 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); */ - virtual void TakeFromPool(const UClass* ObjectClass, const FTransform& Transform = FTransform::Identity, const TFunction& Completed = nullptr); + * 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. */ + virtual FPoolObjectHandle TakeFromPool(const UClass* ObjectClass, const FTransform& Transform = FTransform::Identity, const FOnSpawnCallback& Completed = nullptr); /** A templated alternative to get the object from a pool by class in template. * Is useful in code with code classes, e.g: TakeFromPool(); */ template - void TakeFromPool(const FTransform& Transform = FTransform::Identity, const TFunction& Completed = nullptr) { return TakeFromPool(T::StaticClass(), Transform, Completed); } + FPoolObjectHandle TakeFromPool(const FTransform& Transform = FTransform::Identity, const FOnSpawnCallback& Completed = nullptr) { return TakeFromPool(T::StaticClass(), Transform, Completed); } - /** Is blueprint-overridable version of TakeFromPool() to find object in pool or return null. */ - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pool Manager", meta = (AutoCreateRefTerm = "Transform")) - UObject* TakeFromPoolOrNull(const UClass* ObjectClass, const FTransform& Transform); + /** Is alternative version of TakeFromPool() to find object in pool or return null. */ + virtual const FPoolObjectData* TakeFromPoolOrNull(const UClass* ObjectClass, const FTransform& Transform); public: - /** Returns the specified object to the pool and deactivates it if the object was taken from the pool before. */ + /** 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. + * @return true if pool was found and returned successfully, otherwise false. */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pool Manager", meta = (DefaultToSelf = "Object")) - void ReturnToPool(UObject* Object); - virtual void ReturnToPool_Implementation(UObject* Object); + bool ReturnToPool(UObject* Object); + virtual bool ReturnToPool_Implementation(UObject* Object); + + /** Alternative to ReturnToPool() to return object to the pool by its handle. + * Is useful in case when you don't have a reference to the object but have its handle. + * @param Handle The handle associated with the object to return to the pool. + * @return true if handle was found and return successfully, otherwise false. */ + virtual bool ReturnToPool(const FPoolObjectHandle& Handle); /********************************************************************************************* * Advanced @@ -118,17 +126,19 @@ class POOLMANAGER_API UPoolManagerSubsystem : public UWorldSubsystem * Should not be used directly in most cases since is called automatically. * Could be useful to add already existed objects (spawned by outer code) to the pool. * It's designed to be used only on already existed objects unknown for the Pool Manager. - * @param Object The object to register in the pool. - * @param PoolObjectState The state of the object to register in the pool. */ - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pool Manager", meta = (DefaultToSelf = "Object")) - bool RegisterObjectInPool(UObject* Object = nullptr, EPoolObjectState PoolObjectState = EPoolObjectState::Inactive); - virtual bool RegisterObjectInPool_Implementation(UObject* Object = nullptr, EPoolObjectState PoolObjectState = EPoolObjectState::Inactive); + * @param InData The data with the object to register in the pool. + * @return true if registered successfully, otherwise false. */ + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pool Manager", meta = (AutoCreateRefTerm = "InData")) + bool RegisterObjectInPool(const FPoolObjectData& InData); + virtual bool RegisterObjectInPool_Implementation(const FPoolObjectData& InData); /** Always creates new object and adds it to the pool by its class. - * Use carefully if only there is no free objects contained in pool. */ + * Use carefully if only there is no free objects contained in pool. + * @param InRequest The request to spawn new object. + * @return Handle to the object with the hash associated with object to be spawned next frames. */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pool Manager", meta = (AutoCreateRefTerm = "InRequest")) - void CreateNewObjectInPool(const FSpawnRequest& InRequest); - virtual void CreateNewObjectInPool_Implementation(const FSpawnRequest& InRequest); + FPoolObjectHandle CreateNewObjectInPool(const FSpawnRequest& InRequest); + virtual FPoolObjectHandle CreateNewObjectInPool_Implementation(const FSpawnRequest& InRequest); /********************************************************************************************* * Advanced - Factories @@ -222,6 +232,11 @@ class POOLMANAGER_API UPoolManagerSubsystem : public UWorldSubsystem int32 GetRegisteredObjectsNum(const UClass* ObjectClass) const; virtual int32 GetRegisteredObjectsNum_Implementation(const UClass* ObjectClass) const; + /** Returns the object associated with given handle. + * Can be null if not found or object is in spawning queue. */ + UFUNCTION(BlueprintPure, Category = "Pool Manager") + UObject* FindPoolObjectByHandle(const FPoolObjectHandle& Handle) const; + /********************************************************************************************* * Protected properties ********************************************************************************************* */ diff --git a/Source/PoolManager/Public/PoolManagerTypes.h b/Source/PoolManager/Public/PoolManagerTypes.h index ff851ae..517e750 100644 --- a/Source/PoolManager/Public/PoolManagerTypes.h +++ b/Source/PoolManager/Public/PoolManagerTypes.h @@ -4,6 +4,8 @@ #include "UObject/Object.h" //--- +#include "Misc/Guid.h" +//--- #include "PoolManagerTypes.generated.h" /** @@ -20,6 +22,53 @@ enum class EPoolObjectState : uint8 Active }; +/** + * A handle for managing pool object indirectly. + * - Provides a unique identifier, Hash, with associated object in the pool. + * - Enables tracking and control of objects within the Pool Manager system. + * - Useful in scenarios where object is requested from the pool and the handle is obtained immediately, + * even if the object spawning is delayed to a later frame or different thread. + */ +USTRUCT(BlueprintType) +struct POOLMANAGER_API FPoolObjectHandle +{ + GENERATED_BODY() + + /* Default constructor of empty handle. */ + FPoolObjectHandle() = default; + + /** Empty pool object handle. */ + static const FPoolObjectHandle EmptyHandle; + + /** Generates a new handle for the specified object class. */ + static FPoolObjectHandle NewHandle(const UClass& InObjectClass); + + /** Returns true if Hash is generated. */ + FORCEINLINE bool IsValid() const { return ObjectClass && Hash.IsValid(); } + + /** Empties the handle. */ + void Invalidate() { *this = EmptyHandle; } + + friend POOLMANAGER_API uint32 GetTypeHash(const FPoolObjectHandle& InHandle) { return GetTypeHash(InHandle.Hash); } + friend POOLMANAGER_API bool operator==(const FPoolObjectHandle& Lhs, const FPoolObjectHandle& Rhs) { return Lhs.Hash == Rhs.Hash; } + + /********************************************************************************************* + * Fields + * Is private to prevent direct access to the fields, use NewHandle() instead. + ********************************************************************************************* */ +public: + const UClass* GetObjectClass() const { return ObjectClass; } + const FGuid& GetHash() const { return Hash; } + +private: + /** Class of the object in the pool. */ + UPROPERTY(Transient) + const UClass* ObjectClass = nullptr; + + /** Generated hash for the object. */ + FGuid Hash; +}; + /** * Contains the data that describe specific object in a pool. */ @@ -45,14 +94,21 @@ struct POOLMANAGER_API FPoolObjectData UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient) TObjectPtr PoolObject = nullptr; + /** The handle associated with this pool object for management within the Pool Manager system. */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient) + FPoolObjectHandle Handle = FPoolObjectHandle::EmptyHandle; + /** Returns true if the object is taken from the pool. */ FORCEINLINE bool IsActive() const { return bIsActive && IsValid(); } + /** Returns the state of the object in the pool. */ + EPoolObjectState GetState() const; + /** Returns true if handled object is inactive and ready to be taken from pool. */ FORCEINLINE bool IsFree() const { return !bIsActive && IsValid(); } /** Returns true if the object is created. */ - FORCEINLINE bool IsValid() const { return PoolObject != nullptr; } + FORCEINLINE bool IsValid() const { return PoolObject && Handle.IsValid(); } /** conversion to "bool" returning true if pool object is valid. */ FORCEINLINE operator bool() const { return IsValid(); } @@ -109,16 +165,18 @@ struct POOLMANAGER_API FPoolContainer FORCEINLINE bool IsValid() const { return ObjectClass != nullptr; } }; +typedef TFunction FOnSpawnCallback; + /** * Contains the functions that are called when the object is spawned. */ struct POOLMANAGER_API FSpawnCallbacks { - /** Returns spawned object if it finished spawning successfully. */ - TFunction OnPreConstructed = nullptr; + /** Returns complete object data before registration in the Pool. */ + FOnSpawnCallback OnPreRegistered = nullptr; - /** Returns spawned object when it finished spawning successfully. */ - TFunction OnPostSpawned = nullptr; + /** Returns already spawned and registered object. */ + FOnSpawnCallback OnPostSpawned = nullptr; }; /** @@ -137,6 +195,14 @@ struct POOLMANAGER_API FSpawnRequest UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient) FTransform Transform = FTransform::Identity; - // Contains the functions that are called when the object is spawned + /** The handle associated with spawning pool object for management within the Pool Manager system. + * Is generated automatically if not set. */ + UPROPERTY(BlueprintReadOnly, Transient) + FPoolObjectHandle Handle = FPoolObjectHandle::EmptyHandle; + + /** Contains the functions that are called when the object is spawned. */ FSpawnCallbacks Callbacks; + + /** Returns true if this spawn request can be processed. */ + FORCEINLINE bool IsValid() const { return Class && Handle.IsValid(); } };