일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 배열
- 언리얼엔진5
- 소멸자
- effectivec++
- CPP
- Unreal Engine5
- cpp개발
- 게임개발
- 게임프로그래밍패턴
- 포인터
- 디자인패턴
- AI
- 언리얼엔진
- C언어
- Unreal
- 복사대입연산자
- 자료구조
- 데이터구조
- C++
- 게임 개발
- 프로그래밍
- 언리얼 엔진5
- 언리얼
- BehaviorTree
- 복사생성자
- 프로세스
- Unreal Engine
- 언리얼5
- UE5
- 생성자
- Today
- Total
리얼 개발
[디자인 패턴] ObjectPool Pattern 본문
객체 풀 패턴 (ObjectPool Pattern) 이란
생성 디자인 패턴으로, 필요에 따라 객체를 할당하고 삭제하는
대신 사용할 준비가 된 일련의 초기화된 객체 "풀"을 사용한다.
클라이언트가 객체를 사용해야 하는 상황이 되면 미리 객체를 생성해 모아놓은 "풀"에 객체를 요청하고, 클라이언트가 객체 사용을 완료하면 삭제하는 대신 "풀"에 반환한다.
이러한 객체 풀 패턴은 게임 개발에서 자주 사용하곤 한다. (다른 곳에서는 어떤지 잘 모르겠음)
패턴 사용 상황
내가 탄막 게임을 만드는 개발자라고 생각해보자. 화면을 가득 채우고 있는 탄환들은 어떻게 생성해야 할까?
탄환은 계속해서 이동해야하며, 플레이어가 조종하는 캐릭터와 충돌했을 때 충돌 판정이 필요하다.
이런 탄환들은 하나의 객체로 화면에 생성되고 삭제됨을 반복할 것이다.
ex) 플레이어가 공격을 할 때 플레이어 위치에 탄환 생성 및 탄환이 화면에서 벗어났을 때 탄환 삭제
오브젝트를 생성하기 위해 메모리에 올리고 삭제하기 위해 메모리에서 내리고,,, CPU는 탄환을 생성하고 삭제하는 것 말고도 할 일이 많은데 시간을 뺏기게 될 것이다. 거기다 빈번하게 메모리에 할당 및 삭제하는 과정은 메모리 단편화의 위험성도 커진다.
그래서 객체를 미리 생성해놓고(풀) 빌려쓰고 반환하자! 라는 의미로 객체 풀 패턴을 사용한다.
장점 / 단점
장점
앞서 말한 문제점을 걱정하지 않아도 된다.
단점
미리 생성해놓기에 많은 객체들이 메모리를 차지하고 있는다.
C++ 예시 코드
1. 객체로 사용할 사각형과 삼각형 클래스
게임 개발을 하다보니 빈번하게 생성, 삭제할 객체들은 보통 상위 클래스로 묶어 다형성으로 관리하는 일이 많았다.
그래서 예시로도 다형성으로 관리할 수 있도록 상위 클래스인 Diagram을 상속받고 있다.
#pragma once
#ifndef __DIAGRAM_H__
#define __DIAGRAM_H__
#include <iostream>
class Diagram
{
public:
virtual void Use() = 0;
};
class Rectangle : public Diagram
{
public:
Rectangle()
{
std::cout << "사각형 클래스 생성!" << std::endl;
}
~Rectangle()
{
std::cout << "사각형 클래스 제거!" << std::endl;
}
virtual void Use() override
{
std::cout << "사각형 클래스 사용" << std::endl;
}
};
class Triangle : public Diagram
{
public:
Triangle()
{
std::cout << "삼각형 클래스 생성!" << std::endl;
}
~Triangle()
{
std::cout << "삼각형 클래스 제거!" << std::endl;
}
virtual void Use() override
{
std::cout << "삼각형 클래스 사용" << std::endl;
}
};
#endif // __DIAGRAM_H__
2. 객체 풀 (Object Pool)
삼각형 50개, 사각형 50개를 만든 뒤 저장한다.
풀에 접근할 수 있는 GetObject() 함수 정의 -> 템플릿을 사용하여 Diagram 계통의 모든 객체에 접근할 수 있도록 한다.
ReturnObject를 통해 객체 반납한다.
#pragma once
#include <vector>
#include <typeinfo>
#include <stdexcept>
#include "Diagram.h"
class ObjectPool
{
public:
ObjectPool()
{
Init();
}
~ObjectPool()
{
// 풀에 남아 있는 객체 삭제
for (auto Obj : Pool)
{
delete Obj;
}
Pool.clear();
}
// 객체를 풀에서 가져옴
template<typename T>
T* GetObject()
{
for (size_t i = 0; i < Pool.size(); ++i)
{
T* CastedObj = dynamic_cast<T*>(Pool[i]);
if (CastedObj)
{
// 객체를 반환하고 풀에서 제거
Pool.erase(Pool.begin() + i);
return CastedObj;
}
}
// 해당 타입의 객체가 없는 경우 예외 발생
throw std::runtime_error("현재 풀에서 해당 타입의 객체가 존재하지 않음");
}
// 객체를 풀로 반환
void ReturnObject(Diagram* Obj)
{
Pool.push_back(Obj);
}
private:
void Init()
{
for (int i = 0; i < 100; ++i)
{
if (i < 50)
Pool.push_back(new Rectangle());
else
Pool.push_back(new Triangle());
}
}
std::vector<Diagram*> Pool; // Diagram 기반 객체 저장소
};
3. 메인 실행 함수
#include "ObjectPool.h"
int main()
{
ObjectPool ObjPool;
Diagram* Shape;
try
{
Shape = ObjPool.GetObject<Triangle>();
Shape->Use();
ObjPool.ReturnObject(Shape);
}
catch (std::runtime_error& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
예시 코드가 정확히 맞을지는 모르겠지만, 객체를 빈번하게 생성, 삭제하는 비용이 부담스럽다면 미리 생성해 놓은 뒤 꺼내쓸 수도 있구나란 생각을 가지는 게 포인트인 것 같다.
'디자인 패턴' 카테고리의 다른 글
[게임 디자인 패턴] Part 2. 디자인 패턴 - 옵저버 패턴 (1) | 2024.07.08 |
---|---|
[게임 디자인 패턴] Part 2. 디자인 패턴 - 경량 패턴 (0) | 2024.07.05 |
[게임 디자인 패턴] Part 2. 디자인 패턴 - 명령 패턴 (2) | 2024.07.02 |
[게임 디자인 패턴] Part 1. 도입 (1) | 2024.07.02 |