Skip to content

A high-performance, modular, and Blueprint-friendly object pooling system for Unreal Engine.

License

Notifications You must be signed in to change notification settings

Thyke/ObjectRecycler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Object Recycler

A high-performance, modular, and Blueprint-friendly object pooling system for Unreal Engine.


Read this in other languages: English, Turkish


Overview

Object Recycler is a robust solution for optimizing game performance by recycling spawned objects instead of destroying and recreating them. This significantly reduces CPU and memory overhead, especially in games with large numbers of frequently spawned objects.

Features

  • High Performance: Recycling objects can be up to 45x faster than spawning fresh actors
  • Blueprint-Friendly: Full Blueprint support with an intuitive API
  • Memory Management: Automatic pool size management with configurable settings
  • Developer Settings Integration: Configure pools centrally via Project Settings
  • WorldSubsystem Architecture: Isolated pools per level for better management
  • Automatic Warmup: Pre-populate pools automatically at level start
  • Garbage Collection Optimization: Reduces GC pressure and associated hitches
  • Detailed Logging: Comprehensive debug information for troubleshooting

Installation

  1. Clone this repository
  2. Copy the ObjectRecycler folder to your project's Plugins directory
  3. Regenerate project files and recompile your project
  4. Enable the plugin in your project settings

Alternatively, you can copy the source files directly into your project's source directory.

Basic Usage

Configuring Object Pools

Object pools can be configured globally through the Unreal Editor UI:

  1. Go to Edit → Project Settings → Game → Object Recycler
  2. Enable Auto Warmup
  3. Set Warmup Start Delay (seconds to wait after level start)
  4. Add pool configurations for your object classes
  5. Configure Amount To Spawn, Delay Between Spawns, Soft Cap, and Pool Removal Frequency
# DefaultGame.ini example
[/Script/YourGame.ObjectRecyclerSettings]
bEnableAutoWarmup=True
WarmupStartDelay=1.0
+AutoWarmupPools=(ObjectClass="/Game/Blueprints/BP_Projectile.BP_Projectile_C",AmountToSpawn=50,DelayBetweenSpawns=0.05,SoftCap=500,PoolRemovalFrequency=0.1)

Creating a Poolable Actor

Any actor that implements the IObjectPoolable interface can be managed by the pool system:

UCLASS()
class AMyPoolableActor : public AActor, public IObjectPoolable
{
    GENERATED_BODY()
    
public:
    // IObjectPoolable interface
    virtual void AddedToPool_Implementation(FObjectPoolPayload Payload) override;
    virtual void RemovedFromPool_Implementation(FObjectPoolPayload Payload) override;
    virtual void ObjectRequestedForUsage_Implementation(FObjectPoolPayload Payload) override;
    virtual void ReturnedToPool_Implementation(FObjectPoolPayload Payload) override;
    virtual bool CanBeRecycled_Implementation() override;
};

Implementing the Interface

Implement the required functions to properly handle pooling:

void AMyPoolableActor::AddedToPool_Implementation(FObjectPoolPayload Payload)
{
    // Hide and disable actor when added to pool
    SetActorHiddenInGame(true);
    SetActorEnableCollision(false);
    SetActorTickEnabled(false);
}

void AMyPoolableActor::ObjectRequestedForUsage_Implementation(FObjectPoolPayload Payload)
{
    // Set up actor when taken from pool (like BeginPlay)
    SetActorLocation(Payload.SpawnLocation);
    SetActorRotation(Payload.SpawnRotation);
    SetActorHiddenInGame(false);
    SetActorEnableCollision(true);
    SetActorTickEnabled(true);
    
    // Get custom parameters if provided
    if (Payload.FloatParameters.Contains("Speed"))
    {
        float Speed = Payload.FloatParameters["Speed"];
        // Use the speed parameter
    }
}

void AMyPoolableActor::ReturnedToPool_Implementation(FObjectPoolPayload Payload)
{
    // Clean up actor when returned to pool (like Destroy)
    SetActorHiddenInGame(true);
    SetActorEnableCollision(false);
    SetActorTickEnabled(false);
}

bool AMyPoolableActor::CanBeRecycled_Implementation()
{
    // Return true if this object can be recycled now
    return true;
}

Getting Objects from Pool

// Get the pool manager from world subsystem
UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
if (PoolManager)
{
    // Create payload with spawn parameters
    FObjectPoolPayload Payload;
    Payload.SpawnLocation = SpawnLocation;
    Payload.SpawnRotation = SpawnRotation;
    Payload.FloatParameters.Add("Speed", 2000.0f);
    
    // Get object from pool (will automatically spawn if none available)
    AActor* Actor = PoolManager->GetObjectFromPool(MyActorClass, Payload);
    
    // Cast to your specific actor type if needed
    AMyActor* MyActor = Cast<AMyActor>(Actor);
    if (MyActor)
    {
        // Use the actor...
    }
}

Returning Objects to Pool

// When object is no longer needed (e.g., projectile hit something)
void AMyActor::ReturnToPool()
{
    UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
    if (PoolManager)
    {
        FObjectPoolPayload Payload;
        // Add any parameters needed for cleanup
        
        // Return to pool instead of destroying
        PoolManager->ReturnObjectToPool(this, Payload);
    }
}

Example: Projectile Implementation

Here's a complete example of a poolable projectile:

// PoolableProjectile.h (partial)
UCLASS()
class APoolableProjectile : public AActor, public IObjectPoolable
{
    GENERATED_BODY()
    
public:
    // Components
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UStaticMeshComponent* ProjectileMesh;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UProjectileMovementComponent* ProjectileMovement;
    
    // Properties
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
    float Damage = 20.0f;
    
    UPROPERTY(BlueprintReadOnly, Category = "Pooling")
    bool bIsActive = false;
    
    // Methods
    UFUNCTION()
    void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, 
               UPrimitiveComponent* OtherComp, FVector NormalImpulse, 
               const FHitResult& Hit);
    
    UFUNCTION(BlueprintCallable, Category = "Projectile")
    void ReturnToPool();
};

// PoolableProjectile.cpp (partial)
void APoolableProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, 
                           UPrimitiveComponent* OtherComp, FVector NormalImpulse, 
                           const FHitResult& Hit)
{
    if (bIsActive && OtherActor && OtherActor != this)
    {
        // Apply damage
        UGameplayStatics::ApplyPointDamage(
            OtherActor,                    
            Damage,                        
            Hit.ImpactPoint - Hit.TraceStart, 
            Hit,                           
            GetInstigatorController(),     
            this,                          
            UDamageType::StaticClass()     
        );
        
        // Spawn hit effect
        SpawnHitEffect(Hit.ImpactPoint, Hit.ImpactNormal);
        
        // Return to pool
        ReturnToPool();
    }
}

void APoolableProjectile::ReturnToPool()
{
    if (!bIsActive)
    {
        return;
    }
    
    UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
    if (PoolManager)
    {
        FObjectPoolPayload Payload;
        PoolManager->ReturnObjectToPool(this, Payload);
    }
}

Firing projectiles from a weapon

void UWeaponComponent::Fire()
{
    UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
    if (PoolManager)
    {
        FObjectPoolPayload Payload;
        Payload.SpawnLocation = MuzzleLocation;
        Payload.SpawnRotation = MuzzleRotation;
        Payload.FloatParameters.Add("Damage", ProjectileDamage);
        Payload.FloatParameters.Add("Speed", ProjectileSpeed);
        
        AActor* Actor = PoolManager->GetObjectFromPool(ProjectileClass, Payload);
        APoolableProjectile* Projectile = Cast<APoolableProjectile>(Actor);
        if (Projectile)
        {
            Projectile->FireProjectile(MuzzleRotation.Vector(), ProjectileSpeed);
        }
    }
}

API Reference

UObjectPoolManager (WorldSubsystem)

Method Description
WarmupPool Pre-spawns a number of objects to avoid runtime spawning costs
GetObjectFromPool Retrieves an object from the pool or spawns a new one
ReturnObjectToPool Returns an object to the pool for later reuse
AddObjectToPool Adds an existing object to the pool
RemoveObjectFromPool Permanently removes an object from the pool
LabelObjectAsBusy Marks an object as in-use
LabelObjectAsRecyclable Marks an object as available for reuse
CanObjectBeRecycled Checks if an object can be recycled
ResetObjectPool Clears all objects from a specific pool
ResetClassPool Removes an entire class pool

IObjectPoolable Interface

Method Description
AddedToPool Called when an object is first added to the pool
RemovedFromPool Called when an object is permanently removed from the pool
ObjectRequestedForUsage Called when an object is retrieved from the pool (similar to BeginPlay)
ReturnedToPool Called when an object is returned to the pool (similar to Destroy)
CanBeRecycled Returns whether the object can be recycled

Best Practices

  1. Pre-warm pools for predictable object types at level start
  2. Keep objects simple when pooling - simpler objects recycle faster
  3. Properly reset state in ObjectRequestedForUsage and ReturnedToPool
  4. Use custom parameters in FObjectPoolPayload to pass initialization data
  5. Configure soft caps appropriately based on your game's needs
  6. Use the WorldSubsystem to get access to the pool manager
  7. Add detailed logging when debugging pooling issues

License

This project is licensed under the MIT License - see the LICENSE file for details.


Object Recycler (Türkçe)

Unreal Engine için yüksek performanslı, modüler ve Blueprint-dostu nesne havuzlama sistemi.

Genel Bakış

Object Recycler, spawn edilen nesneleri yok edip yeniden oluşturmak yerine geri dönüştürerek oyun performansını optimize eden sağlam bir çözümdür. Bu, özellikle sık sık nesne spawn edilen oyunlarda CPU ve bellek kullanımını önemli ölçüde azaltır.

Özellikler

  • Yüksek Performans: Nesneleri geri dönüştürmek, yeniden spawn etmekten 45 kata kadar daha hızlı olabilir
  • Blueprint-Dostu: Sezgisel bir API ile tam Blueprint desteği
  • Bellek Yönetimi: Yapılandırılabilir ayarlarla otomatik havuz boyutu yönetimi
  • Developer Settings Entegrasyonu: Proje Ayarları üzerinden merkezi havuz yapılandırması
  • WorldSubsystem Mimarisi: Daha iyi yönetim için seviye bazında izole havuzlar
  • Otomatik Warmup: Seviye başlangıcında havuzları otomatik olarak doldurma
  • Garbage Collection Optimizasyonu: GC baskısını ve ilgili takılmaları azaltır
  • Detaylı Günlük Kaydı: Sorun giderme için kapsamlı hata ayıklama bilgileri

Kurulum

  1. Bu repository'yi klonlayın
  2. ObjectRecycler klasörünü projenizin Plugins dizinine kopyalayın
  3. Proje dosyalarını yeniden oluşturun ve projenizi derleyin
  4. Eklentiyi proje ayarlarınızda etkinleştirin

Alternatif olarak, kaynak dosyalarını doğrudan projenizin kaynak dizinine kopyalayabilirsiniz.

Temel Kullanım

Nesne Havuzlarını Yapılandırma

Nesne havuzları, Unreal Editor arayüzü üzerinden global olarak yapılandırılabilir:

  1. Düzenle → Proje Ayarları → Oyun → Object Recycler'a gidin
  2. Otomatik Isınma'yı etkinleştirin
  3. Isınma Başlangıç Gecikmesi'ni ayarlayın (seviye başlangıcından sonra beklenecek saniye)
  4. Nesne sınıflarınız için havuz yapılandırmaları ekleyin
  5. Spawn Edilecek Miktar, Spawn'lar Arası Gecikme, Soft Cap ve Havuz Kaldırma Sıklığı'nı yapılandırın
# DefaultGame.ini örneği
[/Script/YourGame.ObjectRecyclerSettings]
bEnableAutoWarmup=True
WarmupStartDelay=1.0
+AutoWarmupPools=(ObjectClass="/Game/Blueprints/BP_Projectile.BP_Projectile_C",AmountToSpawn=50,DelayBetweenSpawns=0.05,SoftCap=500,PoolRemovalFrequency=0.1)

Havuzlanabilir Aktör Oluşturma

IObjectPoolable arayüzünü uygulayan herhangi bir aktör, havuz sistemi tarafından yönetilebilir:

UCLASS()
class AMyPoolableActor : public AActor, public IObjectPoolable
{
    GENERATED_BODY()
    
public:
    // IObjectPoolable arayüzü
    virtual void AddedToPool_Implementation(FObjectPoolPayload Payload) override;
    virtual void RemovedFromPool_Implementation(FObjectPoolPayload Payload) override;
    virtual void ObjectRequestedForUsage_Implementation(FObjectPoolPayload Payload) override;
    virtual void ReturnedToPool_Implementation(FObjectPoolPayload Payload) override;
    virtual bool CanBeRecycled_Implementation() override;
};

Arayüzü Uygulama

Havuzlamayı doğru şekilde işlemek için gerekli fonksiyonları uygulayın:

void AMyPoolableActor::AddedToPool_Implementation(FObjectPoolPayload Payload)
{
    // Havuza eklendiğinde aktörü gizle ve devre dışı bırak
    SetActorHiddenInGame(true);
    SetActorEnableCollision(false);
    SetActorTickEnabled(false);
}

void AMyPoolableActor::ObjectRequestedForUsage_Implementation(FObjectPoolPayload Payload)
{
    // Havuzdan alındığında aktörü ayarla (BeginPlay gibi)
    SetActorLocation(Payload.SpawnLocation);
    SetActorRotation(Payload.SpawnRotation);
    SetActorHiddenInGame(false);
    SetActorEnableCollision(true);
    SetActorTickEnabled(true);
    
    // Özel parametreler sağlanmışsa bunları al
    if (Payload.FloatParameters.Contains("Speed"))
    {
        float Speed = Payload.FloatParameters["Speed"];
        // Hız parametresini kullan
    }
}

void AMyPoolableActor::ReturnedToPool_Implementation(FObjectPoolPayload Payload)
{
    // Havuza geri döndüğünde aktörü temizle (Destroy gibi)
    SetActorHiddenInGame(true);
    SetActorEnableCollision(false);
    SetActorTickEnabled(false);
}

bool AMyPoolableActor::CanBeRecycled_Implementation()
{
    // Bu nesnenin şu anda geri dönüştürülebilir olup olmadığını döndür
    return true;
}

Havuzdan Nesne Alma

// World subsystem'den havuz yöneticisini al
UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
if (PoolManager)
{
    // Spawn parametreleriyle payload oluştur
    FObjectPoolPayload Payload;
    Payload.SpawnLocation = SpawnLocation;
    Payload.SpawnRotation = SpawnRotation;
    Payload.FloatParameters.Add("Speed", 2000.0f);
    
    // Havuzdan nesne al (mevcut yoksa otomatik olarak spawn eder)
    AActor* Actor = PoolManager->GetObjectFromPool(MyActorClass, Payload);
    
    // Gerekirse belirli aktör tipine cast et
    AMyActor* MyActor = Cast<AMyActor>(Actor);
    if (MyActor)
    {
        // Aktörü kullan...
    }
}

Nesneleri Havuza Geri Döndürme

// Nesne artık gerekli olmadığında (örn. mermi bir şeye çarptığında)
void AMyActor::ReturnToPool()
{
    UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
    if (PoolManager)
    {
        FObjectPoolPayload Payload;
        // Temizlik için gerekli parametreleri ekle
        
        // Yok etmek yerine havuza geri döndür
        PoolManager->ReturnObjectToPool(this, Payload);
    }
}

Örnek: Mermi İmplementasyonu

İşte havuzlanabilir bir mermi için tam bir örnek:

// PoolableProjectile.h (kısmi)
UCLASS()
class APoolableProjectile : public AActor, public IObjectPoolable
{
    GENERATED_BODY()
    
public:
    // Bileşenler
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UStaticMeshComponent* ProjectileMesh;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UProjectileMovementComponent* ProjectileMovement;
    
    // Özellikler
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
    float Damage = 20.0f;
    
    UPROPERTY(BlueprintReadOnly, Category = "Pooling")
    bool bIsActive = false;
    
    // Metodlar
    UFUNCTION()
    void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, 
               UPrimitiveComponent* OtherComp, FVector NormalImpulse, 
               const FHitResult& Hit);
    
    UFUNCTION(BlueprintCallable, Category = "Projectile")
    void ReturnToPool();
};

// PoolableProjectile.cpp (kısmi)
void APoolableProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, 
                           UPrimitiveComponent* OtherComp, FVector NormalImpulse, 
                           const FHitResult& Hit)
{
    if (bIsActive && OtherActor && OtherActor != this)
    {
        // Hasar uygula
        UGameplayStatics::ApplyPointDamage(
            OtherActor,                    
            Damage,                        
            Hit.ImpactPoint - Hit.TraceStart, 
            Hit,                           
            GetInstigatorController(),     
            this,                          
            UDamageType::StaticClass()     
        );
        
        // Çarpma efekti oluştur
        SpawnHitEffect(Hit.ImpactPoint, Hit.ImpactNormal);
        
        // Havuza geri dön
        ReturnToPool();
    }
}

void APoolableProjectile::ReturnToPool()
{
    if (!bIsActive)
    {
        return;
    }
    
    UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
    if (PoolManager)
    {
        FObjectPoolPayload Payload;
        PoolManager->ReturnObjectToPool(this, Payload);
    }
}

Silahtan mermi ateşleme

void UWeaponComponent::Fire()
{
    UObjectPoolManager* PoolManager = GetWorld()->GetSubsystem<UObjectPoolManager>();
    if (PoolManager)
    {
        FObjectPoolPayload Payload;
        Payload.SpawnLocation = MuzzleLocation;
        Payload.SpawnRotation = MuzzleRotation;
        Payload.FloatParameters.Add("Damage", ProjectileDamage);
        Payload.FloatParameters.Add("Speed", ProjectileSpeed);
        
        AActor* Actor = PoolManager->GetObjectFromPool(ProjectileClass, Payload);
        APoolableProjectile* Projectile = Cast<APoolableProjectile>(Actor);
        if (Projectile)
        {
            Projectile->FireProjectile(MuzzleRotation.Vector(), ProjectileSpeed);
        }
    }
}

API Referansı

UObjectPoolManager (WorldSubsystem)

Metod Açıklama
WarmupPool Çalışma zamanı spawn etme maliyetlerini önlemek için belirli sayıda nesne önceden spawn eder
GetObjectFromPool Havuzdan bir nesne alır veya yeni bir tane spawn eder
ReturnObjectToPool Bir nesneyi daha sonra yeniden kullanılmak üzere havuza geri döndürür
AddObjectToPool Mevcut bir nesneyi havuza ekler
RemoveObjectFromPool Bir nesneyi kalıcı olarak havuzdan kaldırır
LabelObjectAsBusy Bir nesneyi kullanımda olarak işaretler
LabelObjectAsRecyclable Bir nesneyi yeniden kullanım için uygun olarak işaretler
CanObjectBeRecycled Bir nesnenin geri dönüştürülebilir olup olmadığını kontrol eder
ResetObjectPool Belirli bir havuzdaki tüm nesneleri temizler
ResetClassPool Tüm sınıf havuzunu kaldırır

IObjectPoolable Arayüzü

Metod Açıklama
AddedToPool Bir nesne ilk kez havuza eklendiğinde çağrılır
RemovedFromPool Bir nesne kalıcı olarak havuzdan kaldırıldığında çağrılır
ObjectRequestedForUsage Bir nesne havuzdan alındığında çağrılır (BeginPlay'e benzer)
ReturnedToPool Bir nesne havuza geri döndüğünde çağrılır (Destroy'a benzer)
CanBeRecycled Nesnenin geri dönüştürülebilir olup olmadığını döndürür

En İyi Uygulamalar

  1. Tahmin edilebilir nesne türleri için havuzları önceden ısıtın (seviye başlangıcında)
  2. Havuzlanan nesneleri basit tutun - daha basit nesneler daha hızlı geri dönüştürülür
  3. ObjectRequestedForUsage ve ReturnedToPool içinde durumu düzgün şekilde sıfırlayın
  4. Başlatma verisi geçirmek için FObjectPoolPayload içindeki özel parametreleri kullanın
  5. Oyununuzun ihtiyaçlarına göre soft cap'leri uygun şekilde yapılandırın
  6. Havuz yöneticisine erişmek için WorldSubsystem kullanın
  7. Havuzlama sorunlarını ayıklarken detaylı günlük kaydı ekleyin

Lisans

Bu proje MIT Lisansı altında lisanslanmıştır - detaylar için LICENSE dosyasına bakın.

About

A high-performance, modular, and Blueprint-friendly object pooling system for Unreal Engine.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published