[Unity] 다중 오브젝트 풀 ObjectPool

고현규·2024년 1월 29일
0
post-custom-banner

ObjectPoolManager.cs

오브젝트 풀을 관리하는 매니저 입니다. 싱글턴 매니저로 만들었습니다. 유니티에서 제공하는 Pool을 사용합니다.

_poolStringArray에 오브젝트 풀을 사용할 프리팹들의 이름을 모두 넣어 놓습니다.
Dictionary로 프리팹의 이름과 함께 OjbectPool을 저장할 공간을 만들어놓습니다.
Initialize() 에서 List에 해당하는 것들을 생성자로 pool을 생성해서 Dictionary에 저장합니다.

ObjectPool을 사용하려면 GetGo(”생성하려는 프리팹 이름”)함수를 사용해서 만들 수 있습니다.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;

public class ObjectPoolManager
{
    private string objectName;

    private string[] _poolStringArray = new string[4] { "PlayerProjectileFrame", "EnemyProjectileFrame", "FollowerProjectileFrame", "Canvas_FloatingDamage" };

    private Dictionary<string, IObjectPool<GameObject>> poolDict = new Dictionary<string, IObjectPool<GameObject>>();

    public void Initialize()
    {
        for (int i = 0; i < _poolStringArray.Length; i++)
        {
            IObjectPool<GameObject> pool = new ObjectPool<GameObject>(CreateProjectile, OnGetProjectile, OnReleaseProjectile, OnDestroyProjectile, maxSize: 20);

            poolDict.Add(_poolStringArray[i], pool);
        }
    }

    private GameObject CreateProjectile()
    {
        GameObject poolGo = Manager.Resource.InstantiatePrefab(objectName);
        poolGo.GetComponent<ObjectPoolable>().SetManagedPool(poolDict[objectName]);
        return poolGo;
    }

    private void OnGetProjectile(GameObject projectile)
    {
        projectile.SetActive(true);
    }

    private void OnReleaseProjectile(GameObject projectile)
    {
        projectile.SetActive(false);
    }

    private void OnDestroyProjectile(GameObject projectile)
    {
        GameObject.Destroy(projectile);
    }

    public GameObject GetGo(string goName)
    {
        objectName = goName;

        return poolDict[goName].Get();
    }
}

ProjectileHanderBase.cs

공격 발사체가 상속 받는 스크립트입니다.
ObjectPoolable을 상속 받아 오브젝트 풀이 가능해집니다.
Pool 함구가 적용된 부분은 OnTriggerEnter2D 메서드에 ReleaseObject()함수를 사용하고 있습니다.

using UnityEngine;
using UnityEngine.Pool;

public class ProjectileHandlerBase : ObjectPoolable
{
    [HideInInspector]
    public long Damage;
    [HideInInspector]
    public GameObject ProjectileVFX;
    [HideInInspector]
    public DamageType DamageTypeValue = DamageType.Normal;

    public float Speed = 0.1f;

    public Vector2 TargetPosition;

    public LayerMask TargetLayerMask;

    protected virtual void Start()
    {
        if (ProjectileVFX != null)
        {
            Instantiate(ProjectileVFX, transform.position, Quaternion.identity, gameObject.transform);
        }
    }

    protected void TrackingTarget(Vector2 targetPosition, float speed)
    {
        transform.position = Vector2.MoveTowards(transform.position, targetPosition, speed);
    }

    protected virtual void OnTriggerEnter2D(Collider2D collision)
    {
        if (TargetLayerMask.value == (TargetLayerMask.value | (1 << collision.gameObject.layer)))
        {
            ReleaseObject();
        }
    }

    protected virtual void OnTriggerExit2D(Collider2D collision)
    {
        if (TargetLayerMask.value == (TargetLayerMask.value | (1 << collision.gameObject.layer)))
        {
            collision.gameObject.GetComponent<IDamageable>().TakeDamage(Damage, DamageTypeValue);
        }
    }
}

ObjectPoolable.cs

오브젝트 풀을 할 대상이 SetManagedPool()을 통해 Poolabel 인터페이스에 저장한 뒤
이를 Release할 수 있게 만들었습니다.

using UnityEngine;
using UnityEngine.Pool;

public class ObjectPoolable : MonoBehaviour
{
    public IObjectPool<GameObject> Poolable { get; private set; }

    public void SetManagedPool(IObjectPool<GameObject> pool)
    {
        Poolable = pool;
    }

    public void ReleaseObject()
    {
        Poolable.Release(gameObject);
    }
}

Player.cs

Projectile을 생성하려면 싱글턴 ObjectPool을 통해 GetGo을 통해 생성하고자 하는 프리팹 이름을 넘겨줍니다.
그 뒤 생성한 오브젝트의 위치를 지정해줍니다.

public void MakeRangeProjectile()
{
    // 공격 projectile 생성
    var testProjectile = Manager.ObjectPool.GetGo("PlayerProjectileFrame");
    testProjectile.transform.position = ProjectilePoint.position;
}

발생한 문제

  1. 오브젝트 풀을 사용하려는 GameOjbect는 각자 인터페이스 IObjectPool pool을 생성한 뒤, 지정해 두어야 하는데 이를 하지 않아 player와 enemy가 서로 같은 pool을 전달 받아 사용하여 에러가 발생했었다.
  2. DamageFloating도 오브젝트 풀로 사용하려고 해봤으나, 다시 EnemyProjectile이 Release하는 데에서 오류가 발생했다. 아마 생성할 때 매니저 내부 objectName가 동시 다발적으로 접근하면서 처음엔 EnemyProejctile로 접근하다 중간에 FloatingUI로 접근하여 올바르지 않은 접근이 되었을 수 있다.
profile
게임 개발과 기획
post-custom-banner

0개의 댓글