PoolManager.cs
using System.Collections.Generic;
using UnityEngine;
public enum PoolKey
{
BULLET,
MONSTER,
MUZZLEFLASH,
EXPLOSION,
BULLETHOLE
}
public class PoolManager : MonoBehaviour
{
public static PoolManager instance { get; private set; }
[System.Serializable]
public struct PoolData
{
public PoolKey key;
public Transform parentGroup;
public GameObject prefab;
public int count;
}
[SerializeField] private List<PoolData> _poolList;
private Dictionary<PoolKey, Queue<GameObject>> _poolDictionary = new();
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
return;
}
instance = this;
}
void Start()
{
CreatAllPool();
}
private void CreatAllPool()
{
foreach (var data in _poolList)
{
Queue<GameObject> queue = new Queue<GameObject>();
for (int i = 0; i < data.count; i++)
{
GameObject obj = Instantiate(data.prefab, data.parentGroup);
obj.SetActive(false);
queue.Enqueue(obj);
}
_poolDictionary.Add(data.key, queue);
}
}
public GameObject Get(PoolKey key)
{
if (_poolDictionary.ContainsKey(key) == false)
{
Debug.LogWarning(key + "가 적절하지 않습니다.");
return null;
}
if (_poolDictionary[key].Count > 0)
{
GameObject obj = _poolDictionary[key].Dequeue();
obj.SetActive(true);
return obj;
}
GameObject newObj = Instantiate(_poolList[(int)key].prefab);
newObj.SetActive(true);
return newObj;
}
public void Return(GameObject obj, PoolKey key)
{
if (_poolDictionary.ContainsKey(key) == false)
{
Debug.LogWarning(key + "가 적절하지 않습니다.");
return;
}
obj.SetActive(false);
_poolDictionary[key].Enqueue(obj);
}
}
PooledParticle.cs
using System.Collections;
using UnityEngine;
public class PooledParticle : MonoBehaviour
{
private ParticleSystem _particle;
private Coroutine _DoneRoutine;
public PoolKey currentKey;
private void Awake()
{
_particle = GetComponent<ParticleSystem>();
}
void OnEnable()
{
if (_DoneRoutine != null)
{
StopCoroutine(_DoneRoutine);
}
_particle.Clear();
_particle.Play();
_DoneRoutine = StartCoroutine(DoneRoutine());
}
IEnumerator DoneRoutine()
{
while (_particle != null && _particle.IsAlive(true))
{
yield return null;
}
PoolManager.instance.Return(gameObject, currentKey);
}
public void RestartAt(Transform firePosition)
{
transform.SetPositionAndRotation(firePosition.position, firePosition.rotation);
}
}
BulletController.cs (수정 사항)
private void OnCollisionEnter(Collision collision)
{
// 총알 충돌 이펙트
if (collision.gameObject.CompareTag("Player") || collision.gameObject.CompareTag("Enemy"))
{
GameObject explosion = PoolManager.instance.Get(PoolKey.EXPLOSION);
explosion.GetComponent<PooledParticle>().RestartAt(this.transform);
PoolManager.instance.Return(gameObject, PoolKey.BULLET);
}
// 총알 충돌 + 총알 구멍 이펙트
else if (collision.gameObject.CompareTag("Wall"))
{
GameObject explosion = PoolManager.instance.Get(PoolKey.EXPLOSION);
explosion.GetComponent<PooledParticle>().RestartAt(this.transform);
GameObject bulletHole = PoolManager.instance.Get(PoolKey.BULLETHOLE);
bulletHole.GetComponent<PooledParticle>().RestartAt(this.transform);
PoolManager.instance.Return(gameObject, PoolKey.BULLET);
}
}
PoolKey 열거형에 MUZZLEFLASH, EXPLOSION, BULLETHOLE을 추가하여 관리 범위를 넓혔다.public PoolKey currentKey를 선언하여 인스펙터에서 프리팹별로 맞는 키를 할당하고, 파티클 종료 시 자동으로 풀에 반환되도록 구현했다.BulletController에서 충돌 감지 시 PoolManager를 통해 이펙트를 생성하고, 특히 벽(Wall) 충돌 시에는 총알 구멍 이펙트를 추가로 소환하도록 구성했다.OnTriggerEnter 사용 시 충돌 지점이 물체 안쪽에 잡혀 총알 구멍 이펙트가 벽에 파묻혀 보이지 않는 현상 발생.RayCast를 사용하려 했으나, 모든 총알에 개별적으로 레이를 쏘는 것은 연산 비용이 크고 코드가 복잡해질 것이라 판단했다.OnCollisionEnter로 수정함으로써 물리적인 충돌 지점 정보를 더 정확하고 간단하게 가져와 해결했다.Entries에 저장되며, 버킷은 해당 데이터의 주소를 가리킨다.| 메서드 | 설명 |
|---|---|
Add(key, value) | 키와 값을 추가한다. |
ContainsKey(key) | 특정 키의 존재 여부를 확인한다. |
TryGetValue(key, out value) | 키를 확인하고 값을 안전하게 가져온다. |
Remove(key) | 특정 키와 값을 삭제한다. |
foreach로 전수 조사하는 것보다 Dictionary를 사용하는 것이 성능 면에서 훨씬 압도적일 것 같다.private Dictionary<PoolKey, Queue<GameObject>>와 같은 복합적인 문법이 아직 낯설다. 자동완성에 의존하기보다 구조를 완전히 이해하도록 노력해야겠다.