유니티 심화

절대강좌 유니티 - 오브젝트 풀링

다모아 2023. 8. 28. 14:35

사용할 때마다 Instantiate하지말고 미리 만들어놓고 쓰자

 

GameManager // 오브젝트 풀링

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    //public Transform[] points;
    [SerializeField]
    private GameObject monsterPrefab;

    //몬스터 생성 시간
    private float createTime = 3.0f;

    public List<Transform> points = new List<Transform>();

    //게임 오버
    private bool isGameOver = false;

    public bool IsGameOver
    {
        get
        {
            return this.isGameOver;
        }
        set
        {
            this.isGameOver = value;
            if(this.isGameOver)
            {
                CancelInvoke("CreateMonster");
            }
        }
    }

    //싱글턴 인스턴스 선언
    public static GameManager instance = null;

    //몬스터를 미리 생성해 저장할 리스트 자료형
    public List<GameObject> monsterPool = new List<GameObject>();

    //오브젝트 풀에 생성할 몬스터의 최대 개수
    public int maxMonsters = 10;
    
    private void Awake()
    {
        //instance가 할당되지 않았을 경우
        if(instance == null)
        {
            instance = this;
        }
        //instance에 할당된 클래스의 인스턴스가 다를 경우 새로 생성된 클래스를 의미함
        else if(instance != this)
        {
            Destroy(this.gameObject);
        }

        //다른 씬으로 넘어가도 삭제 X 유지
        DontDestroyOnLoad(this.gameObject);
    }
    private void Reset()
    {
        Debug.Log("Reset");
    }
    // Start is called before the first frame update
    void Start()
    {
        this.CreateMonsterPool();

        Transform spawnPointGroup = GameObject.Find("SpawnPointGroup")?.transform;

        Debug.LogFormat("spawnPointGroup: {0}", spawnPointGroup);

        spawnPointGroup?.GetComponentsInChildren<Transform>(true, this.points);

        Debug.LogFormat("points.Count: {0}", this.points.Count);

        foreach (Transform child in spawnPointGroup)
        {
            //Debug.Log(child);
            points.Add(child);
        }

        //2초 후에 createTime(3초)마다 호출, 시작 시에는 2초 후 호출
        InvokeRepeating("CreateMonster", 2.0f, this.createTime);
    }

    private void CreateMonster()
    {
        //생성할 위치/ 회전 정보 필요
        int index = Random.Range(0, this.points.Count);
        Transform point = this.points[index];
        //몬스터 프리팹의 인스턴스를 생성
        //Instantiate(this.monsterPrefab, point.position, point.rotation);
        
        //풀에서 비활성화된 오브젝트 가져오기
        GameObject monster = this.GetMonsterInPool();
        //위치, 회전 적용
        monster.transform.SetPositionAndRotation(point.position, point.rotation);
        //활성화
        monster.SetActive(true);
        
    }

    private GameObject GetMonsterInPool()
    {
        foreach (GameObject monster in this.monsterPool)
        {
            if (monster.activeSelf == false)
            {
                return monster;
            }
        }
        return null;
    }

    private void CreateMonsterPool()
    {
        for (int i = 0; i < this.maxMonsters; i++)
        {
            GameObject monster = Instantiate(this.monsterPrefab);
            monster.name = $"Monster_{i:00}"; // Monster_00 ~ Monster_01...
            monster.SetActive(false);
            this.monsterPool.Add(monster);
        }
    }
}

1. 풀에 만들어서 미리 비활성화 시킴

 

2. 풀에서 비활성화 되어있는 오브젝트 찾아서 활성화 시킴

 

3. 풀에 반납

 

ObjectPoolingMain

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ObjectPoolingMain : MonoBehaviour
{
    //버튼가져오기
    [SerializeField]
    private Button btn;
    [SerializeField]
    private BulletPoolManager bulletPoolManager;
    // Start is called before the first frame update
    void Start()
    {
        //버튼 누르면
        this.btn.onClick.AddListener(() => {
            GameObject bullet = this.bulletPoolManager.GetBulletInPool();
            bullet.transform.position = Vector3.zero;
            bullet.SetActive(true);
        });
    }

    private void Shoot()
    {
        Debug.Log("Shoot");
    }
}

 

TestBullet

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

public class TestBullet : MonoBehaviour
{

    // Update is called once per frame
    void Update()
    {
        this.transform.Translate(this.transform.forward * 1f * Time.deltaTime);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if(collision.collider.CompareTag("Wall"))
        {
            BulletPoolManager.instance.BulletStop(this.gameObject);
        }
    }
}

 

BulletPoolManager

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

public class BulletPoolManager : MonoBehaviour
{
    public static BulletPoolManager instance;
    //총알프리팹
    [SerializeField]
    private GameObject bulletPrefab;
    public List<GameObject> bulletPool = new List<GameObject>();
    //총알 최대치 설정하기
    private int maxBullets = 10;

    private void Awake()
    {
        instance = this;

        DontDestroyOnLoad(this.gameObject);
    }

    // Start is called before the first frame update
    void Start()
    {
        for (int i = 0; i < this.maxBullets; i++)
        {
            GameObject bullet = Instantiate(this.bulletPrefab);
            bullet.SetActive(false);
            bullet.transform.SetParent(this.transform);
            this.bulletPool.Add(bullet);
        }
    }
    
    public GameObject GetBulletInPool()
    {
        foreach (GameObject bullet in bulletPool)
        {
            if(bullet.activeSelf == false)
            {
                return bullet;
            }
        }
        return null;
    }

    public void BulletStop(GameObject bullet)
    {
        bullet.SetActive(false);
        bullet.transform.SetParent(this.transform);
    }
}