본 게시글은 면접준비 및 자기계발을 목적으로 작성된 게시글입니다
공부한 내용을 토대로 남들에게 설명할 수 있도록 이해하는 과정에 작성한 게시글이니 참고바랍니다
Object
를 담아둘 Object Pool
을 만들고 빌려줄 오브젝트를 생성 후 오브젝트 풀이 그 오브젝트를 관리하도록 함📌 가비지 컬렉팅(Garbage Collecting)이란?
Object
를 Destroy할 때 메모리에서 곧바로 사라지는것이 아니라, 게임에서 보이지 않게 되지만Garbage Collector
가 그것을 수거해서 파괴하기 전엔 메모리에 남아있게 됨Object
를 Destroy하면 일정 사이클이 지난 후에Garbage Collector
가 메모리를 뒤져서 이것을 수거- 파괴된 객체가 많으면 그 순간 모두 정리하려 하기 때문에 게임의 프레임이 일시적으로 올라감
간략하게 설명, 이후 Garbage Collecting에 대한 자세한 게시글 작성 예정
실습에 사용된 오브젝트는 Shooter
, Bullet(Prefab)
, ObjectPool
이 있다
Shooter
: Bullet을 발사하는 객체, 화면 클릭시 해당 방향으로 Bullet을 발사
Bullet
: 화면을 클릭시 해당 방향으로 날아가는 객체, Prefab으로 생성
ObectPool
: Object들을 관리하는 Object Pool
Object Pooling
기법을 사용하기에 앞서 해당 방법을 사용하지 않았을 시에 발생하는 문제점을 파악했다
오브젝트 풀링으로 Bullet을 생성하는것이 아닌
클릭시 Instantiate로 Bullet을 생성하고 일정시간 뒤에 Destroy를 통해 오브젝트를 파괴하였다
위 이미지는 가비지 컬렉터의 프레임이 튀는 이미지이다
해당 방법으로 Bullet을 생성했을 시에 프로파일러를 확인해본 결과
Bullet을 생성과 파괴에 CPU가 일정 시간 할당되고 일정 주기로 Garbage Collector
의 프레임이 스파이크 튀는것을 확인할 수 있었다
따라서 해당 문제를 해결하기 위해 Object Pooling 기법을 적용해보았다
//Shooter 클래스
public class Shooter : MonoBehaviour
{
[SerializeField] private GameObject bulletPrefab;
private Camera mainCam;
private void Start()
{
mainCam = Camera.main;
}
//화면 클릭시 해당 방향으로 Bullet 생성
private void Update()
{
if(Input.GetMouseButton(0))
{
Ray ray = mainCam.ScreenPointToRay(Input.mousePosition);
var direction = new Vector3(ray.origin.x, ray.origin.y, 0) - transform.position;
var bullet = ObjectPool.GetObject();
bullet.transform.position = transform.position + direction.normalized;
bullet.Shoot(direction.normalized);
}
}
}
//Bullet 클래스
public class Bullet : MonoBehaviour
{
private Vector2 direction;
//Shoot Bullet
public void Shoot(Vector2 dir)
{
direction = dir;
Invoke("DestroyBullet", 5f);
}
private void DestroyBullet()
{
ObjectPool.ReturnObject(this);
}
private void Update()
{
transform.Translate(direction);
}
}
//ObjectPool 클래스
public class ObjectPool : MonoBehaviour
{
public static ObjectPool Instance;
[SerializeField]
private GameObject poolingObjectPrefab;
//Pool에 있는 객체들을 Queue로 관리
private Queue<Bullet> poolingObjectQueue = new Queue<Bullet>();
private void Awake()
{
Instance = this;
Initialize(10); //초기 객체의 수를 10으로 설정
}
//새로운 객체를 생성
private Bullet CreateNewObject()
{
var newObj = Instantiate(poolingObjectPrefab, transform).GetComponent<Bullet>();
newObj.gameObject.SetActive(false);
return newObj;
}
private void Initialize(int count)
{
for(int i=0;i<count;i++)
{
poolingObjectQueue.Enqueue(CreateNewObject());
}
}
public static Bullet GetObject()
{
if(Instance.poolingObjectQueue.Count > 0) //Pool에 객체가 있으면 Pool에 있는 객체 재사용
{
var obj = Instance.poolingObjectQueue.Dequeue();
obj.transform.SetParent(null);
obj.gameObject.SetActive(true);
return obj;
}
else //Pool에 객체가 없으면 새로운 객체 생성
{
var newObj = Instance.CreateNewObject();
newObj.transform.SetParent(null);
newObj.gameObject.SetActive(true);
return newObj;
}
}
//객체 사용 후 Pool에 반납
public static void ReturnObject(Bullet bullet)
{
bullet.gameObject.SetActive(false);
bullet.transform.SetParent(Instance.transform);
Instance.poolingObjectQueue.Enqueue(bullet);
}
}
화면 클릭시 오브젝트가 생성될 때 발사할때마다 새로 생성되는것이 아닌
Object Pool에 있는 객체를 활성화하며 우선적으로 사용한 후에 수가 부족할 시 새롭게 생성되는 결과를 확인