개요
Object Pooling은 게임 개발에서 자주 사용되는 메모리 관리 기법 중 하나입니다. 이는 객체를 반복적으로 생성하고 삭제하는 비용을 줄이고 성능을 향상시키기 위해 미리 일정 개수의 객체를 생성하여 재사용하는 방식입니다. Unreal Engine에서는 Actor를 생성하고 삭제하는 비용이 크기 때문에, Object Pooling을 활용하면 퍼포먼스를 최적화할 수 있습니다. 이번 글에서는 Unreal Engine C++을 사용하여 Object Pooling을 구현하는 방법을 소개하겠습니다.
Object Pooling 개념
Object Pooling은 다음과 같은 원리로 작동합니다:
- 객체 미리 생성 - 특정 개수의 객체를 미리 생성하여 Pool에 저장합니다.
- 재사용 - 필요할 때 Pool에서 객체를 가져와 활성화합니다.
- 반환 - 사용이 끝난 객체를 다시 Pool에 반환하여 재사용할 수 있도록 합니다.
이 방식은 특히 다음과 같은 경우 유용합니다:
- 총알, 아이템, 이펙트 등 짧은 시간 동안 반복적으로 생성 및 제거되는 객체 관리
- 성능 최적화가 필요한 모바일 및 콘솔 게임
- 네트워크 동기화가 중요한 멀티플레이어 게임
1. Object Pool Manager 생성하기
Object Pool을 관리하는 UItemPoolManager 클래스를 작성합니다. 이 클래스는 아이템을 미리 생성하고 필요할 때 재사용할 수 있도록 관리합니다.
#include "Items/Manager/ItemPoolManager.h"
#include "Items/Class/SpawnableItem.h"
#include "Engine/World.h"
void UItemPoolManager::InitializePool(TSubclassOf<ASpawnableItem> ItemClass, int32 PoolSize)
{
if (!GetWorld() || ItemPool.Num() > 0) return; // 이미 풀 초기화됨
SpawnableItemClass = ItemClass;
for (int32 i = 0; i < PoolSize; ++i)
{
ASpawnableItem* NewItem = GetWorld()->SpawnActor<ASpawnableItem>(ItemClass);
if (NewItem)
{
NewItem->SetActorHiddenInGame(true);
NewItem->SetActorEnableCollision(false);
ItemPool.Add(NewItem);
}
}
}
ASpawnableItem* UItemPoolManager::GetItemFromPool()
{
for (ASpawnableItem* Item : ItemPool)
{
if (Item && Item->IsHidden())
{
Item->SetActorHiddenInGame(false);
Item->SetActorEnableCollision(true);
return Item;
}
}
return nullptr;
}
void UItemPoolManager::ReturnItemToPool(ASpawnableItem* Item)
{
if (Item)
{
Item->SetActorHiddenInGame(true);
Item->SetActorEnableCollision(false);
ItemPool.Add(Item);
}
}
2. 아이템 클래스 구현하기
아이템 클래스 ASpawnableItem을 작성하여 오버랩 이벤트와 상호작용 로직을 구현합니다.
#include "Items/Class/SpawnableItem.h"
#include "Components/SphereComponent.h"
#include "Kismet/GameplayStatics.h"
#include "NiagaraFunctionLibrary.h"
ASpawnableItem::ASpawnableItem()
{
PrimaryActorTick.bCanEverTick = false;
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(Scene);
Collision = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionSphere"));
Collision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
Collision->SetupAttachment(Scene);
StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ItemMesh"));
StaticMesh->SetupAttachment(Collision);
StaticMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Collision->OnComponentBeginOverlap.AddDynamic(this, &ASpawnableItem::OnItemOverlap);
}
void ASpawnableItem::OnItemOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor && OtherActor->ActorHasTag("Player"))
{
Interact(OtherActor);
}
}
void ASpawnableItem::Interact(AActor* Activator)
{
if (HasAuthority() && Activator)
{
Multicast_OnInteract();
ResetItem();
}
}
void ASpawnableItem::ResetItem()
{
SetActorHiddenInGame(true);
SetActorEnableCollision(false);
UItemPoolManager* PoolManager = GetGameInstance()->GetSubsystem<UItemPoolManager>();
if (PoolManager)
{
PoolManager->ReturnItemToPool(this);
OnItemResetDelegate.Broadcast();
}
}
3. 아이템 스포너 구현하기
아이템을 생성하고 일정 시간 후 다시 풀로 반환하는 역할을 수행하는 AItemSpawner 클래스를 작성합니다.
#include "Items/Class/ItemSpawner.h"
#include "Items/Manager/ItemPoolManager.h"
AItemSpawner::AItemSpawner()
{
PrimaryActorTick.bCanEverTick = false;
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(Scene);
}
void AItemSpawner::BeginPlay()
{
Super::BeginPlay();
UItemPoolManager* PoolManager = GetGameInstance()->GetSubsystem<UItemPoolManager>();
if (PoolManager && ItemClass)
{
PoolManager->InitializePool(ItemClass, 4);
SpawnItem();
}
}
void AItemSpawner::SpawnItem()
{
UItemPoolManager* PoolManager = GetGameInstance()->GetSubsystem<UItemPoolManager>();
if (PoolManager)
{
ASpawnableItem* Item = PoolManager->GetItemFromPool();
if (Item)
{
FVector SpawnLocation = GetActorLocation();
SpawnLocation.Z += 100.0f;
Item->SetActorLocation(SpawnLocation);
Item->SetActorHiddenInGame(false);
Item->SetActorEnableCollision(true);
Item->OnItemResetDelegate.AddDynamic(this, &AItemSpawner::OnItemReset);
bIsItemActive = true;
}
}
}
void AItemSpawner::OnItemReset()
{
GetWorldTimerManager().SetTimer(SpawnTimerHandle, this, &AItemSpawner::SpawnItem, SpawnCooldown, false);
bIsItemActive = false;
}
'TIL > C++와 UE' 카테고리의 다른 글
언리얼엔진 멀티플레이에서의 PlayerState (0) | 2025.04.08 |
---|---|
GameInstanceSubsystem (0) | 2025.04.02 |
언리얼 엔진 레플리케이션(Replication) (0) | 2025.03.11 |
네트워킹 및 멀티 플레이어 개요 (0) | 2025.03.10 |
작성중 (0) | 2025.02.26 |