이번 팀 프로젝트에서 우리팀은 '닷지' 게임을 우리만의 방식으로 다시 만들어보는 프로젝트를 진행하였다.
개발 시작전에 팀끼리 토의를 하면서 위의 그림과 같이 와이어 프레임을 설정하고 게임 개발에 들어갔다.
이번 프로젝트에서 나는 총알 및 투사체 구현기능을 맡게 되었는데, 생각해야 될 문제들이 꽤나 많았다.
우리가 구현하려고 한 게임에서는 구조적으로 플레이어 및 적 유닛들이 총알을 굉장히 많이 발사하게 될 수 밖에 없는데, 이렇게 되면 문제가 총알 오브젝트들이 계속 생성과 파괴가 반복되면서 메모리가 많이 사용될 수 밖에 없다는 문제가 있었다.
거기서 사용하게 된 것이 이번에 알게 된 오브젝트 풀 패턴이다.
오브젝트 풀 패턴이란?
쉽게 말해서 재사용 할수 있는 객체를 '오브젝트 풀' 안에 넣어 두고 마치 실제로 오브젝트가 생성되고 사라지는 것처럼 꺼내서 사용하는 것이다.
실제로 오브젝트가 생성되고 파괴되는 것이 아니다 보니, 메모리를 생각하지 않고 마음껏 사용 할 수 있다는 장점이 있지만 반대로 미리 오브젝트를 할당해 두고 있다는 점에서 아무것도 사용하지 않아도 메모리를 사용하게 된다는 단점이 있다.
public class ObjectPool : MonoBehaviour
{
private static ObjectPool _i;//접근이 용이한 싱글톤
public static ObjectPool Instance
{
get
{
if (_i == null)
{
_i = FindObjectOfType<ObjectPool>();
if (_i == null)
Debug.Log($"오브젝트 풀이 없습니다.");
}
return _i;
}
}
public GameObject BaseBullet;
private Queue<Bullet> _poolingObjQueue = new Queue<Bullet>();//큐 이용
private void Awake()
{
if (_i == null)
_i = this;
else if (_i != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
private void Start()
{
MakeObjects(20);
}
private Bullet CreateObj()//총알 생성
{
Bullet bullet = Instantiate(BaseBullet, transform).GetComponent<Bullet>();
bullet.gameObject.SetActive(false);
return bullet;
}
public void MakeObjects(int count)//오브젝트 풀에 총알 장전
{
for (int i = 0; i < count; i++)
{
_poolingObjQueue.Enqueue(CreateObj());
}
}
{
Bullet obj = Instance._poolingObjQueue.Dequeue();
obj.transform.SetParent(null);
obj.gameObject.SetActive(true);
return obj;
}
else
{
Bullet newObj = Instance.CreateObj();
newObj.transform.SetParent(null);
newObj.gameObject.SetActive(true);
return newObj;
}
}
public Bullet GetObject()//총알을 발사할때 오브젝트 풀안에서 꺼내오고 부족하면 새로 생성
{
if (Instance._poolingObjQueue.Count > 0)
{
Bullet obj = Instance._poolingObjQueue.Dequeue();
obj.transform.SetParent(null);
obj.gameObject.SetActive(true);
return obj;
}
else
{
Bullet newObj = Instance.CreateObj();
newObj.transform.SetParent(null);
newObj.gameObject.SetActive(true);
return newObj;
}
}
public void ReturnObj(Bullet bullet)//발사된 총알을 다시 오브젝트 풀안으로 반환
{
bullet.gameObject.SetActive(false);
bullet.transform.position = Vector2.zero;
bullet.transform.SetParent(Instance.transform);
Instance._poolingObjQueue.Enqueue(bullet);
}
public void Clear()
{
foreach (var bullet in _poolingObjQueue)
Destroy(bullet);
}
}
위의 코드는 이번 프로젝트에서 내가 직접 작성한 오브젝트 풀 패턴의 코드 이다.
우선 오브젝트 풀의 경우 오브젝트를 생성하는 어떤 코드에서든 접근이 가능해야하는 경우가 많기 때문에 싱글톤 패턴으로 구현하였다.
또, 풀에서 오브젝트를 꺼내주고 돌려받는 순서 관리에 용이한 Queue를 이용하였다.(선입 선출구조)
구조를 확인해보면 CreateObj 메서드와 MakeObjects 메서드를 통해서 오브젝트 풀안에 미리 총알을 장전해두고, GetObjects 메서드를 통해서 총알이 직접 생성되는 대신 오브젝트 풀안에서 꺼내오는 방식으로 총알을 발사하고 ReturnObj 메서드를 통해 오브젝트가 오브젝트 풀안으로 다시 들어감으로 마치 파괴되는 것처럼 만들어 주는 구조로 되어 있다.