https://github.com/onestone3647/Portfolio_ProjectReplica
ObjectPool로 관리하는 오브젝트들을 보다 효율적으로 관리하기 위해 PoolableInterface 인터페이스 클래스를 도입했습니다. 이를 통해 오브젝트 관리를 보다 체계적으로 할 수 있게 되었습니다.
기능 설명
- BaseObjectPoolSystem 또는 BaseObjectPoolSystem을 상속한 ActorComponent 클래스는 PoolableInterface 인터페이스를 상속하는 오브젝트를 ObjectPool 패턴으로 관리합니다.
- DataTable의 정보를 기반으로 오브젝트 풀의 제거 및 초기화를 수행합니다.
- 보관하는 오브젝트의 활성화 및 비활성화를 관리합니다.
- 동적으로 생성한 오브젝트의 Index와 수명을 관리합니다.
PoolableInterface
BaseObjectPoolSystem 또는 BaseObjectPoolSystem을 상속한 ActorComponent 클래스에서 관리하는 오브젝트들은 PoolableInterface 인터페이스 클래스를 상속하여야 합니다. PoolableInterface는 BaseObjectPoolSystem에서 관리할 수 있는 오브젝트들이 따라야 하는 인터페이스를 정의합니다. PoolableInterface는 다음과 같은 역할을 합니다.
인터페이스 정의 및 함수 선언
모든 함수는 블루프린트에서 호출 및 오버라이딩을 할 수 있도록 UFUNCTION의 BlueprintCallable, BlueprintNativeEvent를 사용하였습니다.
활성화 여부 확인
/** 활성화 되었는지 확인하는 함수입니다. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Poolable")
bool IsActivate() const;
virtual bool IsActivate_Implementation() const = 0;
활성화 및 비활성화
/** 활성화하는 함수입니다. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Poolable")
void Activate();
virtual void Activate_Implementation() = 0;
/** 비활성화하는 함수입니다. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Poolable")
void Deactivate();
virtual void Deactivate_Implementation() = 0;
Pool Index
/** PoolIndex를 반환하는 함수입니다. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Poolable")
int32 GetPoolIndex() const;
virtual int32 GetPoolIndex_Implementation() const = 0;
수명 관리
/** 수명을 반환하는 함수입니다. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Poolable")
float GetLifespan() const;
virtual float GetLifespan_Implementation() const = 0;
/**
* 수명을 설정하는 함수입니다.
*
* @param NewLifespan 설정할 수명입니다.
*/
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Poolable")
void SetLifespan(float NewLifespan);
virtual void SetLifespan_Implementation(float NewLifespan) = 0;
BaseObjectPoolSystem
오브젝트 풀링 시스템을 관리하는 기본 ActorComponent 클래스입니다. 이 클래스는 오브젝트 풀링을 효율적으로 관리하기 위해 설계되었으며, 오브젝트의 활성화, 비활성화, 수명 관리, 그리고 동적 오브젝트 제거 등의 기능을 구현했습니다.
주요 변수
// BaseObjectPoolSystem.h
protected:
/**
* 동적으로 생성한 오브젝트의 수명입니다.
* 동적으로 생성한 오브젝트가 비활성화 되었을 때, 해당 시간이 지난 후 오브젝트를 제거합니다.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PRBaseObjectPoolSystem")
float DynamicLifespan;
/** 동적으로 생성하는 ObjectPool의 PoolSize입니다. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PRBaseObjectPoolSystem", meta = (ClampMin = "1"))
int32 DynamicPoolSize;
관련 구조체
// CommonStruct.h
/**
* 활성화된 Index를 보관하는 구조체입니다.
*/
USTRUCT(Atomic, BlueprintType)
struct FPRActivateIndexList
{
GENERATED_BODY()
public:
/** 활성화된 인덱스를 보관하는 Set입니다. */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "ActivateIndexList")
TSet<int32> Indexes;
};
/**
* 이전에 사용된 Index를 보관하는 구조체입니다.
*/
USTRUCT(Atomic, BlueprintType)
struct FPRUsedIndexList
{
GENERATED_BODY()
public:
/** 이전에 사용된 Index를 보관하는 Set입니다. */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "UsedIndexList")
TSet<int32> Indexes;
};
// BaseObjectPoolSystem.h
/**
* 클래스별로 활성화된 오브젝트들의 Index를 보관하는 구조체입니다.
*/
USTRUCT(Atomic, BlueprintType)
struct FPRActivateObjectIndexList
{
GENERATED_BODY()
public:
FPRActivateObjectIndexList()
: List()
{}
FPRActivateObjectIndexList(const TMap<TSubclassOf<UObject>, FPRActivateIndexList>& NewList)
: List(NewList)
{}
public:
/** 클래스 레퍼런스와 활성화된 Index를 보관하는 Map입니다. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PRAcitvateObjectIndexList")
TMap<TSubclassOf<UObject>, FPRActivateIndexList> List;
public:
/**
* 주어진 오브젝트에 해당하는 Indexes를 반환하는 함수입니다.
*
* @param ObjectToFind Indexes를 찾을 오브젝트입니다.
* @return Indexes를 찾을 경우 Indexes를 반환합니다. 못찾았을 경우 nullptr을 반환합니다.
*/
TSet<int32>* GetIndexesForObject(UObject* ObjectToFind)
{
if(!IsValid(ObjectToFind))
{
return nullptr;
}
TSubclassOf<UObject> ActorClass = ObjectToFind->GetClass();
FPRActivateIndexList* ActivateIndexList = List.Find(ActorClass);
if(ActivateIndexList)
{
return &ActivateIndexList->Indexes;
}
return nullptr;
}
};
/**
* 클래스별로 이전에 사용된 오브젝트들의 Index를 보관하는 구조체입니다.
*/
USTRUCT(Atomic, BlueprintType)
struct FPRUsedObjectIndexList
{
GENERATED_BODY()
public:
FPRUsedObjectIndexList()
: List()
{}
FPRUsedObjectIndexList(const TMap<TSubclassOf<UObject>, FPRUsedIndexList>& NewList)
: List(NewList)
{}
public:
/** 클래스 레퍼런스와 해당 클래스의 이전에 사용된 Index를 보관하는 Map입니다. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PRUsedObjectIndexList")
TMap<TSubclassOf<UObject>, FPRUsedIndexList> List;
};
/**
* 동적으로 생성한 오브젝트와 해당 오브젝트를 제거하는 TimerHandle을 관리하는 구조체입니다.
*/
USTRUCT(Atomic, BlueprintType)
struct FPRDynamicDestroyObject
{
GENERATED_BODY()
public:
FPRDynamicDestroyObject()
: TimerHandles()
{}
FPRDynamicDestroyObject(const TMap<TObjectPtr<UObject>, FTimerHandle>& NewTimerHandles)
: TimerHandles(NewTimerHandles)
{}
public:
/** 오브젝트와 해당 오브젝트를 제거하는 TimerHandle을 보관하는 Map입니다. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PRDynamicDestroyObject")
TMap<TObjectPtr<UObject>, FTimerHandle> TimerHandles;
};
/**
* 동적으로 생성한 오브젝트 목록을 클래스별로 보관하는 구조체입니다.
*/
USTRUCT(Atomic, BlueprintType)
struct FPRDynamicDestroyObjectList
{
GENERATED_BODY()
public:
FPRDynamicDestroyObjectList()
: List()
{}
FPRDynamicDestroyObjectList(const TMap<TSubclassOf<UObject>, FPRDynamicDestroyObject>& NewList)
: List(NewList)
{}
public:
/** 클래스 레퍼런스와 동적으로 생성한 오브젝트와 해당 오브젝트를 제거하는 TimerHandle을 보관한 Map입니다. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PRDynamicDestroyObjectPool")
TMap<TSubclassOf<UObject>, FPRDynamicDestroyObject> List;
public:
/**
* 주어진 오브젝트에 해당하는 TimerHandle을 반환하는 함수입니다.
*
* @param ObjectToFind TimerHandle을 찾을 오브젝트입니다.
* @return TimerHandle을 찾았을 경우 TimerHandle을 반환합니다. 못 찾았을 경우 nullptr을 반환합니다.
*/
FTimerHandle* FindTimerHandleForObject(UObject& ObjectToFind)
{
if(!IsValid(&ObjectToFind))
{
return nullptr;
}
FPRDynamicDestroyObject* DestroyObjects = List.Find(ObjectToFind.GetClass());
if(DestroyObjects)
{
FTimerHandle* FoundTimerHandle = DestroyObjects->TimerHandles.Find(ObjectToFind);
if(FoundTimerHandle)
{
return FoundTimerHandle;
}
}
return nullptr;
}
};
주요 역할 및 기능
오브젝트 풀 초기화 및 제거
// BaseObjectPoolSystem.h
public:
/** 기존의 ObjectPool을 제거하고, 새로 ObjectPool을 생성하여 초기화하는 함수입니다. */
UFUNCTION(Blueprintable, Category = "PRBaseObjectPoolSystem")
virtual void InitializeObjectPool();
// BaseObjectPoolSystem.cpp
void UPRBaseObjectPoolSystemComponent::InitializeObjectPool()
{
// 자식 클래스에서 오버라이딩하여 사용합니다.
}
InitializeObjectPool 함수는 기존의 오브젝트 풀을 제거하고 새로 오브젝트 풀을 생성하여 초기화하는 함수입니다. BaseObjectPoolSystem을 상속하는 자식 클래스에서 오버라이딩하여 사용합니다.
// BaseObjectPoolSystem.h
public:
/** 모든 ObjectPool을 제거하는 함수입니다. */
UFUNCTION(Blueprintable, Category = "PRBaseObjectPoolSystem")
virtual void ClearAllObjectPool();
// BaseObjectPoolSystem.cpp
void UPRBaseObjectPoolSystemComponent::ClearAllObjectPool()
{
// 자식 클래스에서 오버라이딩하여 사용합니다.
}
ClearAllObjectPool 함수는 모든 오브젝트 풀을 제거하는 함수입니다. BaseObjectPoolSystem을 상속하는 자식 클래스에서 오버라이딩하여 사용합니다.
풀링 가능한 오브젝트 확인
// BaseObjectPoolSystem.h
public:
/**
* 주어진 객체가 유효한 풀링 가능한 객체인지 확인하는 함수입니다.
*
* @param PoolableObject 확인할 객체입니다.
* @return 객체가 유효하고 PRPoolableInterface를 구현하는 경우 true를 반환합니다. 그렇지 않으면 false를 반환합니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
bool IsPoolableObject(UObject* PoolableObject) const;
/**
* 주어진 오브젝트의 클래스가 유효한 풀링 가능한 오브젝트 클래스인지 확인하는 함수입니다.
*
* @param PoolableObjectClass 확인할 오브젝트 클래스입니다.
* @return 오브젝트의 클래스가 유효하고 PRPoolableInterface를 구현하는 경우 true를 반환합니다. 그렇지 않으면 false를 반환합니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
bool IsPoolableObjectClass(TSubclassOf<UObject> PoolableObjectClass) const;
// BaseObjectPoolSystem.cpp
bool UPRBaseObjectPoolSystemComponent::IsPoolableObject(UObject* PoolableObject) const
{
return IsValid(PoolableObject) && PoolableObject->GetClass()->ImplementsInterface(UPRPoolableInterface::StaticClass());
}
bool UPRBaseObjectPoolSystemComponent::IsPoolableObjectClass(TSubclassOf<UObject> PoolableObjectClass) const
{
return IsValid(PoolableObjectClass) && PoolableObjectClass->ImplementsInterface(UPRPoolableInterface::StaticClass());
}
각 함수는 입력 받은 인자가 유효하고, PoolableInterface를 상속하고 있는지 ImplementsInterface 함수로 확인하여 풀링 가능한지 확인하는 함수입니다.
오브젝트 활성화 및 비활성화
// BaseObjectPoolSystem.h
public:
/**
* 주어진 객체를 활성화하는 함수입니다.
*
* @param PoolableObject 활성화할 객체입니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
void ActivateObject(UObject* PoolableObject);
/**
* 주어진 객체를 비활성화하는 함수입니다.
*
* @param PoolableObject 비활성화할 객체입니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
void DeactivateObject(UObject* PoolableObject);
// BaseObjectPoolSystem.cpp
void UPRBaseObjectPoolSystemComponent::ActivateObject(UObject* PoolableObject)
{
if(IsPoolableObject(PoolableObject))
{
IPRPoolableInterface::Execute_Activate(PoolableObject);
}
}
void UPRBaseObjectPoolSystemComponent::DeactivateObject(UObject* PoolableObject)
{
if(IsPoolableObject(PoolableObject))
{
IPRPoolableInterface::Execute_Deactivate(PoolableObject);
}
}
주어진 객체를 활성화하거나 비활성화하는 함수입니다. 주어진 객체가 유효한 풀링 가능한 오브젝트인지 확인한 후, 조건이 충족되면 PoolableInterface의 Activate 함수 또는 Deactivate 함수를 실행합니다.
오브젝트 속성 관리
// BaseObjectPoolSystem.h
public:
/**
* 주어진 객체의 PoolIndex를 반환하는 함수입니다.
*
* @param PoolableObject PoolIndex를 반환할 객체입니다.
* @return 주어진 객체가 유효하고 풀링 가능한 객체일 경우 객체의 PoolIndex를 반환합니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
int32 GetPoolIndex(UObject* PoolableObject) const;
/**
* 주어진 객체의 Lifespan을 반환하는 함수입니다.
*
* @param PoolableObject Lifespan을 반환할 객체입니다.
* @return 주어진 객체가 유효하고 풀링 가능한 객체일 경우 객체의 Lifespan을 반환합니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
float GetLifespan(UObject* PoolableObject) const;
/**
* 주어진 객체의 Lifespan을 설정하는 함수입니다.
*
* @param PoolableObject Lifespan을 설정할 객체입니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
void SetLifespan(UObject* PoolableObject, float NewLifespan);
// BaseObjectPoolSystem.cpp
float UPRBaseObjectPoolSystemComponent::GetLifespan(UObject* PoolableObject) const
{
if(IsPoolableObject(PoolableObject))
{
return IPRPoolableInterface::Execute_GetLifespan(PoolableObject);
}
return INDEX_NONE;
}
void UPRBaseObjectPoolSystemComponent::SetLifespan(UObject* PoolableObject, float NewLifespan)
{
if(IsPoolableObject(PoolableObject))
{
IPRPoolableInterface::Execute_SetLifespan(PoolableObject, NewLifespan);
}
}
int32 UPRBaseObjectPoolSystemComponent::GetPoolIndex(UObject* PoolableObject) const
{
if(IsPoolableObject(PoolableObject))
{
return IPRPoolableInterface::Execute_GetPoolIndex(PoolableObject);
}
// 유효하지 않은 Index를 반환합니다.
return INDEX_NONE;
}
주어진 객체가 유요한 풀링 가능한 오브젝트인지 확인한 후, 조건이 충족되면 해당 PoolableInterface의 함수를 실행하는 함수들입니다.
사용 가능한 Index 찾기
// BaseObjectPoolSystem.h
public:
/**
* 사용 가능한 Index를 찾아 반환하는 함수입니다.
*
* @param UsedIndexes 이미 사용 중인 Index 목록입니다.
* @return 사용 가능한 Index를 반환합니다.
*/
UFUNCTION(BlueprintCallable, Category = "PRBaseObjectPoolSystem")
int32 FindAvailableIndex(const TSet<int32>& UsedIndexes);
// BaseObjectPoolSystem.cpp
int32 UPRBaseObjectPoolSystemComponent::FindAvailableIndex(const TSet<int32>& UsedIndexes)
{
int32 NewIndex = 0;
while(UsedIndexes.Contains(NewIndex))
{
NewIndex++;
}
return NewIndex;
}
주어진 Set을 순회하며 사용 가능한 Index를 찾아 반환하는 함수입니다.
동적으로 생성한 오브젝트 관리
// BaseObjectPoolSystem.h
protected:
/**
* 동적으로 생성한 오브젝트을 제거하는 함수입니다.
*
* @param TargetDynamicDestroyObjectList 제거할 동적으로생성한 오브젝트의 목록입니다.
*/
UFUNCTION(Blueprintable, Category = "PRBaseObjectPoolSystem")
virtual void ClearDynamicDestroyObjectList(FPRDynamicDestroyObjectList& TargetDynamicDestroyObjectList);
protected:
/**
* Template 함수를 사용하여 동적으로 생성한 오브젝트들을 제거하는 함수입니다.
*
* @tparam KeyType Map의 키 값의 유형입니다. ObjectPoolSystem마다 다를 수 있습니다.
* @param List 동적으로 생성한 오브젝트의 목록입니다.
*/
template <typename KeyType>
void ClearDynamicDestroyObjects(TMap<KeyType, FPRDynamicDestroyObject>& List)
{
for(auto& ListEntry : List)
{
FPRDynamicDestroyObject& DynamicDestroyObject = ListEntry.Value;
if(&DynamicDestroyObject)
{
// DynamicDestroyObject의 모든 타이머를 해제하고 오브젝트를 제거합니다.
for(auto& TimerEntry : DynamicDestroyObject.TimerHandles)
{
if(IsValid(TimerEntry.Key))
{
// 타이머를 해제합니다.
GetWorld()->GetTimerManager().ClearTimer(TimerEntry.Value);
// 오브젝트를 제거합니다.
TimerEntry.Key->ConditionalBeginDestroy(); // 오브젝트를 안전하게 제거하는 함수입니다. 가비지 컬렉션 대상이 되기 전에 수동으로 메모리에서 해제합니다.
TimerEntry.Key = nullptr;
}
}
DynamicDestroyObject.TimerHandles.Empty();
}
}
List.Empty();
}
// BaseObjectPoolSystem.cpp
void UPRBaseObjectPoolSystemComponent::ClearDynamicDestroyObjectList(FPRDynamicDestroyObjectList& TargetDynamicDestroyObjectList)
{
ClearDynamicDestroyObjects(TargetDynamicDestroyObjectList.List);
}
동적으로 생성된 오브젝트들은 활성화된 후 비활성화 상태가 되면 각 ObjectPoolSystem의 동적으로 생성된 오브젝트와 TimerHandle을 보관하는 Map에 저장하게 됩니다. 이 Map의 키 값은 ObjectPoolSystem마다 유형이 다를 수 있기 때문에, Template 함수를 사용하여 동적으로 생성된 오브젝트들을 제거하는 로직을 구현하였습니다.
'Project > Replica' 카테고리의 다른 글
[Project Replica] PoolableInterface / EffectSytstem (0) | 2024.06.29 |
---|---|
[Project Replica] EffectSystem (0) | 2024.04.30 |
[Project Replica] Vaulting / Vault / 장애물 뛰어넘기 (0) | 2024.04.15 |
[Project Replica] DamageSystem 대미지 시스템 (0) | 2024.01.11 |
[Project Replica] AI 생성 시스템 AISpawnSystem (1) | 2023.12.04 |