반응형
개요
프로젝트에서 구현한 풀링 시스템을 소개하는 글이다
내용
ObjectPoolBase
UnityEngine.Pool.ObjectPool을 기반으로 한 오브젝트 풀링 관리 추상 클래스다
제약 조건을 걸어 Component 타입을 내부에서 사용할 수 있게 하였다
일관된 패턴을 사용함으로서 개발자의 러닝 커브를 줄이고 필요에 따라 메서드를 오버라이드하여 특정 풀링 로직을 커스텀할 수 있게 하였다
public abstract class ObjectPoolBase<T> : MonoBehaviourBase where T : Component
{
private ObjectPool<T> objectPool;
[SerializeField] private T prefab;
[SerializeField] private int defaultCapacity = 10;
[SerializeField] private int maxSize = 20;
protected virtual void Awake()
{
objectPool = new ObjectPool<T>(
CreatePooledItem,
OnTakeFromPool,
OnReturnedToPool,
OnDestroyPoolObject,
true,
defaultCapacity,
maxSize);
}
protected virtual T CreatePooledItem()
{
return Instantiate(prefab);
}
protected virtual void OnTakeFromPool(T item)
{
item.gameObject.SetActive(true);
}
protected virtual void OnReturnedToPool(T item)
{
item.gameObject.SetActive(false);
}
protected virtual void OnDestroyPoolObject(T item)
{
Destroy(item.gameObject);
}
public T GetObject()
{
return objectPool.Get();
}
public void ReleaseObject(T item)
{
objectPool.Release(item);
}
}
// 예시: UnitBasePool
public class UnitBasePool : ObjectPoolBase<UnitRoot>
{
private UnitDependencyContainer _dependencyContainer;
public void Init(RootManager rootManager)
{
_dependencyContainer = new UnitDependencyContainer(rootManager);
}
protected override UnitRoot CreatePooledItem()
{
var enemy = base.CreatePooledItem();
enemy.CreatePooledItemInit(_dependencyContainer);
return enemy;
}
}
PooledEntityRootBase
풀에서 가져온 엔티티의 기본 동작을 정의하는 추상 클래스를 정의하였다
public abstract class PooledEntityRootBase : MonoBehaviourBase
{
public abstract void CreatePooledItemInit(DependencyContainerBase containerBase);
public abstract void OnTakeFromPoolInit(EPlayerSide side);
}
public class UnitRoot : PooledEntityRootBase
{
// ...
public override void CreatePooledItemInit(DependencyContainerBase containerBase)
{
UnitDependencyContainer container = containerBase as UnitDependencyContainer;
AssertHelper.NotNull(typeof(UnitRoot), container);
dependencyContainer = container;
_btController = new UnitBTController(this);
spriteController.CreatePooledItemInit(this);
attackController.CreatePooledItemInit(this);
_moveController.CreatePooledItemInit(this);
skillController.CreatePooledItemInit(this);
}
public void SetupUnitClassification(EUnitGrade unitGrade, EUnitType unitType)
{
AssertHelper.NotEqualsEnum(typeof(EnemySpawnHandler),unitGrade, EUnitGrade.None);
AssertHelper.NotEqualsEnum(typeof(EnemySpawnHandler),unitType, EUnitType.None);
grade = unitGrade;
type = unitType;
}
public override void OnTakeFromPoolInit(EPlayerSide side)
{
AssertHelper.NotEqualsEnum(typeof(EnemySpawnHandler),side, EPlayerSide.None);
AssertHelper.NotEqualsEnum(typeof(EnemySpawnHandler),grade, EUnitGrade.None);
AssertHelper.NotEqualsEnum(typeof(EnemySpawnHandler),type, EUnitType.None);
spriteController.ChangeVisible();
_btController.StartBtTick();
attackController.ChangeAttackData(this);
_moveController.ChangeEffectScale();
skillController.SetCurrentSkillGrade(this);
}
// ...
}
// UnitSpawnHandler 내부
private void SpawnUnit(SUnitSpawnRequestData data)
{
UnitRoot unit = _unitBasePool.GetObject();
AssertHelper.NotNull(typeof(UnitSpawnHandler), unit);
unit.SetupUnitClassification(data.UnitGrade, data.UnitType);
unit.OnTakeFromPoolInit(data.SpawnSide);
// ...
}
풀에서 생성될 때 필요한 참조를 셋업하고, 풀에서 나올 때(Get) 등급과 타입에 따라 데이터를 세팅하도록 구현하였다
예를 들어 UnitSpriteController에서는
public void ChangeVisible()
{
UnitMetaData metaData = _mdl.GetResource(_unitRoot.grade, _unitRoot.type);
AssertHelper.NotNull(typeof(UnitAttackController), metaData);
Material material = _mdl.GetUnitGradeMaterial(_unitRoot.grade);
AssertHelper.NotNull(typeof(UnitAttackController), material);
_spriteRenderer.sprite = metaData.Sprite;
_spriteRenderer.material = material;
_spriteRenderer.transform.localScale = Vector3.one * metaData.ScaleSize;
_animator.runtimeAnimatorController = metaData.AnimatorController;
}
이런식으로 적절한 리소스가 세팅된다
마무리
이번 풀링 시스템 구현에서는 객체의 재사용성을 극대화하면서, 필요한 설정을 자동으로 적용할 수 있도록 설계했다
- ObjectPoolBase<T>를 통해 일관된 패턴을 유지하면서도, 필요한 경우 오버라이드하여 커스텀할 수 있도록 구현했다
- PooledEntityRootBase를 도입하여, 풀에서 꺼낼 때 필요한 초기화 로직을 엔티티 별로 정의할 수 있도록 했다
- UnitSpriteController와 같은 개별 컨트롤러에서 풀링된 객체의 상태를 설정하여, 객체가 풀링될 때마다 적절한 데이터를 적용하도록 했다
이러한 구조를 통해 객체 생성/삭제 비용을 줄이고, 유지보수를 용이하게 만들었다
이 풀링 시스템을 기반으로, 향후 더 많은 유닛과 에너미를 관리할 때도 일관된 방식으로 확장할 수 있는 기반을 마련했다
'프로젝트 > Lucky Defense (InGame Clone)' 카테고리의 다른 글
운빨존많겜 인게임 클론 프로젝트 소개 (0) | 2025.03.14 |
---|---|
AI Player 동작 구현 (0) | 2025.03.14 |
유닛 배치 필드 구현 (0) | 2025.03.14 |
에너미와 유닛 프리팹 구조 (0) | 2025.03.14 |
UniRx와 MVP 패턴을 이용한 UI 시스템 (0) | 2025.03.14 |