ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

Actor Pooling: Object Pool Manager

C++ (Unreal)

Goal -- WPM

Ready
Exercise Algorithm Area
1class UObjectPoolManager : public UObject
2{
3GENERATED_BODY()
4
5public:
6// The class of actor to be pooled.
7UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Object Pool")
8TSubclassOf<AActor> PooledActorClass;
9
10// The maximum number of actors allowed in the pool.
11UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Object Pool")
12int32 MaxPoolSize = 50;
13
14// The initial number of actors to pre-spawn.
15UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Object Pool")
16int32 InitialPoolSize = 10;
17
18// Delegate to be called when an actor is activated from the pool.
19DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActorActivated, AActor*, ActivatedActor);
20UPROPERTY(BlueprintAssignable, Category = "Object Pool")
21FOnActorActivated OnActorActivated;
22
23// Delegate to be called when an actor is deactivated and returned to the pool.
24DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActorDeactivated, AActor*, DeactivatedActor);
25UPROPERTY(BlueprintAssignable, Category = "Object Pool")
26FOnActorDeactivated OnActorDeactivated;
27
28UObjectPoolManager()
29{
30// Ensure PooledActorClass is set by the user.
31}
32
33// Initializes the object pool by pre-spawning actors.
34UFUNCTION(BlueprintCallable, Category = "Object Pool")
35void InitializePool(UWorld* World);
36
37// Retrieves an actor from the pool.
38// If no actors are available, it may spawn a new one if MaxPoolSize is not reached.
39// Returns a valid AActor pointer or nullptr if the pool is exhausted.
40UFUNCTION(BlueprintCallable, Category = "Object Pool")
41AActor* GetPooledActor(FVector Location, FRotator Rotation);
42
43// Returns an actor to the pool.
44// The actor will be deactivated and hidden.
45UFUNCTION(BlueprintCallable, Category = "Object Pool")
46void ReturnPooledActor(AActor* ActorToReturn);
47
48// Clears the pool and destroys all pooled actors.
49UFUNCTION(BlueprintCallable, Category = "Object Pool")
50void ClearPool();
51
52private:
53// Stores available actors that can be reused.
54TArray<AActor*> AvailableActors;
55
56// Stores actors that are currently in use.
57TArray<AActor*> ActiveActors;
58
59// The world context for spawning actors.
60UWorld* OwningWorld = nullptr;
61
62// Helper function to spawn a single actor.
63AActor* SpawnActor(FVector Location, FRotator Rotation);
64
65// Helper function to reset an actor's state before returning it to the pool.
66void ResetActorState(AActor* Actor);
67
68// Helper function to clean up an actor when it's permanently removed from the pool.
69void DestroyActor(AActor* Actor);
70};
71
72void UObjectPoolManager::InitializePool(UWorld* World)
73{
74if (!World || !PooledActorClass)
75{
76UE_LOG(LogTemp, Error, TEXT("ObjectPoolManager: Cannot initialize pool. World or PooledActorClass is invalid."));
77return;
78}
79OwningWorld = World;
80
81// Pre-spawn initial actors.
82for (int32 i = 0; i < InitialPoolSize; ++i)
83{
84if (AvailableActors.Num() < MaxPoolSize)
85{
86AActor* NewActor = SpawnActor(FVector::ZeroVector, FRotator::ZeroRotator);
87if (NewActor)
88{
89AvailableActors.Add(NewActor);
90// Initially hide and deactivate them.
91NewActor->SetActorHiddenInGame(true);
92NewActor->SetActorEnableCollision(false);
93NewActor->SetActorTickEnabled(false);
94}
95}
96else
97{
98break; // Reached max pool size during initialization.
99}
100}
101UE_LOG(LogTemp, Log, TEXT("ObjectPoolManager: Initialized with %d actors. Max size: %d."), AvailableActors.Num(), MaxPoolSize);
102}
103
104AActor* UObjectPoolManager::SpawnActor(FVector Location, FRotator Rotation)
105{
106if (!OwningWorld || !PooledActorClass)
107{
108return nullptr;
109}
110FActorSpawnParameters SpawnParams;
111SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
112AActor* NewActor = OwningWorld->SpawnActor<AActor>(PooledActorClass, Location, Rotation, SpawnParams);
113if (NewActor)
114{
115// Perform initial setup for the spawned actor if needed.
116// For example, getting a specific component.
117}
118return NewActor;
119}
120
121AActor* UObjectPoolManager::GetPooledActor(FVector Location, FRotator Rotation)
122{
123AActor* ActorToActivate = nullptr;
124
125// Try to get an actor from the available pool.
126if (AvailableActors.Num() > 0)
127{
128ActorToActivate = AvailableActors.Pop();
129ActiveActors.Add(ActorToActivate);
130}
131else if (ActiveActors.Num() < MaxPoolSize)
132{
133// If pool is empty but not at max size, spawn a new one.
134ActorToActivate = SpawnActor(Location, Rotation);
135if (ActorToActivate)
136{
137ActiveActors.Add(ActorToActivate);
138}
139}
140else
141{
142// Pool is full and no actors are available.
143UE_LOG(LogTemp, Warning, TEXT("ObjectPoolManager: Pool exhausted. Cannot retrieve actor."));
144return nullptr;
145}
146
147if (ActorToActivate)
148{
149// Reset actor state for reuse.
150ActorToActivate->SetActorLocation(Location);
151ActorToActivate->SetActorRotation(Rotation);
152ActorToActivate->SetActorHiddenInGame(false);
153ActorToActivate->SetActorEnableCollision(true);
154ActorToActivate->SetActorTickEnabled(true);
155// Call a specific reset function on the actor if it exists.
156// e.g., if (ActorToActivate->Implements<IResettableActor>()) { IResettableActor::Execute_Reset(ActorToActivate); }
157OnActorActivated.Broadcast(ActorToActivate);
158}
159
160return ActorToActivate;
161}
162
163void UObjectPoolManager::ReturnPooledActor(AActor* ActorToReturn)
164{
165if (!ActorToReturn || !ActiveActors.Contains(ActorToReturn))
166{
167// Actor is not managed by this pool or is already returned.
168return;
169}
170
171// Remove from active list and add to available list.
172ActiveActors.Remove(ActorToReturn);
173AvailableActors.Add(ActorToReturn);
174
175// Reset actor state and hide it.
176ResetActorState(ActorToReturn);
177ActorToReturn->SetActorHiddenInGame(true);
178ActorToReturn->SetActorEnableCollision(false);
179ActorToReturn->SetActorTickEnabled(false);
180
181OnActorDeactivated.Broadcast(ActorToReturn);
182}
183
184void UObjectPoolManager::ResetActorState(AActor* Actor)
185{
186if (!Actor)
187{
188return;
189}
190// Reset basic transform.
191Actor->SetActorLocation(FVector::ZeroVector);
192Actor->SetActorRotation(FRotator::ZeroRotator);
193
194// Reset components or specific actor properties.
195// This is highly dependent on the type of actor being pooled.
196// For example, resetting health, velocity, timers, etc.
197// A common pattern is to have a virtual Reset() function on the pooled actor class.
198
199// Example: If the pooled actor has a UHealthComponent, reset its health.
200// UHealthComponent* HealthComp = Actor->FindComponentByClass<UHealthComponent>();
201// if (HealthComp)
202// {
203// HealthComp->ResetHealthToMax(); // Assuming such a function exists
204// }
205}
206
207void UObjectPoolManager::ClearPool()
208{
209for (AActor* Actor : AvailableActors)
210{
211DestroyActor(Actor);
212}
213AvailableActors.Empty();
214
215for (AActor* Actor : ActiveActors)
216{
217DestroyActor(Actor);
218}
219ActiveActors.Empty();
220
221OwningWorld = nullptr;
222UE_LOG(LogTemp, Log, TEXT("ObjectPoolManager: Pool cleared."));
223}
224
225void UObjectPoolManager::DestroyActor(AActor* Actor)
226{
227if (Actor && Actor->IsValidLowLevel())
228{
229Actor->Destroy();
230}
231}
Algorithm description viewbox

Actor Pooling: Object Pool Manager

Algorithm description:

This C++ code implements an `UObjectPoolManager` for Unreal Engine, designed to efficiently manage a pool of reusable actors. Instead of constantly spawning and destroying actors (which can be performance-intensive), this system pre-allocates a set of actors and reuses them. When an actor is needed, it's retrieved from the pool, its state is reset, and it's activated. When no longer needed, it's returned to the pool, deactivated, and hidden. This is commonly used for projectiles, enemies, or other frequently created/destroyed game entities.

Algorithm explanation:

The `UObjectPoolManager` utilizes two `TArray`s: `AvailableActors` for actors ready to be reused, and `ActiveActors` for actors currently in use. `InitializePool` pre-spawns a number of actors up to `InitialPoolSize` and adds them to `AvailableActors`, hiding and deactivating them. `GetPooledActor` first checks `AvailableActors`. If empty, it checks if `MaxPoolSize` is reached; if not, it spawns a new actor. The retrieved actor is then moved to `ActiveActors`, its transform is set, and it's made visible and enabled. `ReturnPooledActor` removes the actor from `ActiveActors`, adds it to `AvailableActors`, resets its state via `ResetActorState`, and hides/disables it. `ResetActorState` is a crucial helper that must be customized based on the pooled actor's type to ensure it's in a clean state for reuse. `ClearPool` destroys all actors managed by the pool. The time complexity for `GetPooledActor` is O(1) on average if actors are available in the pool or if spawning is not limited by `MaxPoolSize`. If the pool is exhausted and `MaxPoolSize` is reached, it returns `nullptr` in O(1). `ReturnPooledActor` is O(1) on average due to `TArray::Remove` and `TArray::Add`. `InitializePool` is O(I) where I is `InitialPoolSize`, and `ClearPool` is O(N) where N is the total number of actors in the pool. Space complexity is O(M) where M is `MaxPoolSize`.

Pseudocode:

Class ObjectPoolManager:
  PooledActorClass (Class)
  MaxPoolSize (int)
  InitialPoolSize (int)
  AvailableActors (Array of Actors)
  ActiveActors (Array of Actors)
  OwningWorld (World)

  Function InitializePool(World):
    Set OwningWorld = World
    For i from 0 to InitialPoolSize-1:
      If AvailableActors.Count < MaxPoolSize:
        Actor = SpawnActor(ZeroLocation, ZeroRotation)
        If Actor is valid:
          Add Actor to AvailableActors
          Hide and Deactivate Actor

  Function SpawnActor(Location, Rotation):
    Spawn actor of PooledActorClass at Location, Rotation
    Return spawned actor

  Function GetPooledActor(Location, Rotation):
    If AvailableActors is not empty:
      Actor = Remove last from AvailableActors
      Add Actor to ActiveActors
    Else if ActiveActors.Count < MaxPoolSize:
      Actor = SpawnActor(Location, Rotation)
      If Actor is valid:
        Add Actor to ActiveActors
      Else:
        Return null
    Else:
      Return null // Pool exhausted

    If Actor is valid:
      Set Actor Location, Rotation
      Make Actor Visible and Enabled
      Broadcast OnActorActivated
    Return Actor

  Function ReturnPooledActor(ActorToReturn):
    If ActorToReturn is not in ActiveActors:
      Return
    Remove ActorToReturn from ActiveActors
    Add ActorToReturn to AvailableActors
    ResetActorState(ActorToReturn)
    Hide and Deactivate ActorToReturn
    Broadcast OnActorDeactivated

  Function ResetActorState(Actor):
    // Reset actor's properties (health, position, etc.)

  Function ClearPool():
    For each Actor in AvailableActors:
      Destroy Actor
    Empty AvailableActors
    For each Actor in ActiveActors:
      Destroy Actor
    Empty ActiveActors
    Set OwningWorld = null