The code below works for a very simple implementation of object pooling, but it’s limited to spawning the type statically defined at compile time.
The problems is that I’m having an impossible time figuring out how to make the object pool more flexible/generic. It seems logical to be able to pass the type of any arbitrary subclass of AActor to a function and have it be able to create a container of the arbitrary subclass type, AND additionally spawn the AActors of the same arbitrary subclass type.
One work around would be to create a simple AActor container and cast the objects to their correct type at runtime, but it seems counterintuitive to be casting each actor to it’s appropriate type at runtime when I know exactly what the type will be at compile time; my gut is telling me that this is a problem best solved at compile time.
Another workaround is to create a series of overloaded functions that take a different “dummy pointer” of the subclass type I want spawned and then return with a container of the same subclass type. My gut is again telling me that this is a very error prone and bloated/redundant/brute-forced approach to solving this problem and that there has to be a more elegant solution.
I’m new to UE4 and C++ and I understand that C++ is statically typed, but would this be an application for virtual functions since MyPoolingManager won’t know the type at compile time, but me the programmer knows exactly what it will expect, or am I totally off base with that idea/concept?
Any help is greatly appreciated and thanks in advance!
This is mostly boilerplate code, so stripped out all but important bits, and MyActor is just an empty/generic class.
MyCharacter.h
#pragma once
#include "GameFramework/Character.h"
#include "MyPoolingManager.h"
#include "MyCharacter.generated.h"
UCLASS()
class TESTPROJECT_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
AMyCharacter();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Object Pool")
AMyPoolingManager* MyActorPool;
//...
};
MyCharacter.cpp
#include "TestProject.h"
#include "MyCharacter.h"
//...
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
MyActorPool = ()->SpawnActor<AMyPoolingManager>(AMyPoolingManager::StaticClass());
MyActorPool->CreatePool(200);
}
//...
MyPoolingManager.h
#pragma once
#include "GameFramework/Actor.h"
#include "MyPoolingManager.generated.h"
USTRUCT(Blueprintable)
struct FSimplePoolStruct
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Object Pool")
TArray<AActor*> PoolArray;
UPROPERTY(EditAnywhere, Category = "Object Pool")
int32 Next; // Hold the index of the next available actor
FSimplePoolStruct() {
PoolArray.Empty();
Next = 0;
}
};
UCLASS()
class TESTPROJECT_API AMyPoolingManager : public AActor
{
GENERATED_BODY()
public:
AMyPoolingManager();
//...
UFUNCTION()
void CreatePool(int32 PoolSize);
UFUNCTION()
AActor* GetActor();
UPROPERTY(EditAnywhere, Category = "Object Pool")
FSimplePoolStruct MyPool;
};
MyPoolingManager.cpp
#include "TestProject.h"
#include "MyActor.h"
#include "MyPoolingManager.h"
//...
void AMyPoolingManager::CreatePool(int32 PoolSize)
{
UWorld* const World = ();
if (World) {
MyPool.PoolArray.Reserve(20); // Do the memory allocation up front to avoid multiple allocations while iterating through the loop
for (int32 i = 0; i < 20; i++){
MyActor* NewActor = World->SpawnActor<MyActor>(MyActor::StaticClass());
MyPool.PoolArray.Add(NewActor);
}
}
}
AActor* AMyPoolingManager::GetActor(){
return MyPool.PoolArray[MyPool.Next];
MyPool.Next++;
}