251112 Object Pool

2025. 11. 12. 11:28·📖TIL

오브젝트 풀

  1. 오브젝트 풀 개념을 익힌다
  2. 배열과 리스트로 SetActive(true) , (false)
  3. 심화 : 제네릭 버전으로 만든다
  4. 모든걸 다 액션 기반으로 등록할 수 있는 시스템 구축

 

퍼포먼스 올리기

  1. Update 를 덜 돌게 하기
  2. Instantiate / Destroy 만 덜 하게 해도 성능 개선에 도움이 된다

내부단편화 : 할당된 메모리 공간 안에서 사용되지 않고 남는 공간이 생기는 현상

외부단편화 : 총 여유 메모리는 충분하지만 연속된 큰 공간이 부족해서 새 프로세스를 올릴수 없는 현상

 

 

▼ 오브젝트 풀링 구현 코드

using NUnit.Framework;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem; // 좌클릭 등으로 발사하게 하려고함

public class FireLogic : MonoBehaviour
{
    [Header("발사 관련 설정")]
    [SerializeField] GameObject bulletPrefab;
    [SerializeField] int initalPoolSize = 10;
    [SerializeField] float bulletSpeed = 20f;

    private List<GameObject> bulletPool;
    private InputAction fireAction;

    void OnShoot(InputAction.CallbackContext ctx) // 인풋시스템을 사용하기 위한 메서드
    {
        FireBullet();
    }

    private void Awake() // 오브젝트 풀 초기 세팅, 풀에 수많은 총알을 시작과 동시에 담는다
    {
        bulletPool = new List<GameObject>();
        for(int i = 0; i < initalPoolSize; i++)
        {
            CreateNewBullet();
        }
        fireAction = InputSystem.actions["Attack"];
    }
    
 
    private void OnEnable()
    {
        fireAction.performed += OnShoot;
    }

    private void OnDisable() // 구독이 있으면 항상 구독 해제도 만들면 좋다
    {
        fireAction.performed -= OnShoot;
    }

    void Update()
    {
        
    }

    GameObject CreateNewBullet() // bullet 새로 만들고, 이걸 Pool에 담는다
    {
        GameObject newBullet = Instantiate(bulletPrefab); // 그냥 만든거
        newBullet.SetActive(false); // 만들자마자 비활성화
        bulletPool.Add(newBullet); // 관리중인 리스트에 담을 것
        return newBullet;
    }
    void FireBullet()
    {
        GameObject bullet = GetInActiveBullet();
        if (bullet == null)
        {
            bullet = CreateNewBullet();
        }
        bullet.transform.position = transform.position + transform.forward * 2; // 총알의 위치 = 발사자 위치에서 살짝 앞에
        bullet.transform.rotation = transform.rotation; // 발사자의 로테이션
        bullet.SetActive(true); // 풀에서 사용 중인 것 활성화

        bullet.GetComponent<Rigidbody>().linearVelocity = transform.forward * bulletSpeed;
    }

    GameObject GetInActiveBullet()
    {
        foreach (GameObject bullet in bulletPool)
        {
            if (!bullet.activeInHierarchy) // activeSelf
            {
                return bullet;
            }
        }

        return null;
    }
}
using UnityEngine;

public class BulletController : MonoBehaviour
{
    [SerializeField] public float lifeTime = 3f;
    public Rigidbody rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }

    private void OnEnable()
    {
        Invoke(nameof(DisableBullet), lifeTime);
    }

    private void OnDisable()
    {
        rb.linearVelocity = Vector3.zero; // 이전 속도가 남아있을 수도 있어서 zero 로 초기화
        rb.angularVelocity = Vector3.zero;
        CancelInvoke(); // 모든 인보크 없애버린다
    }

    private void OnBecameInvisible() // 어느 카메라에서도 이걸 볼 수 없게 되었을 때
    {
        DisableBullet();
    }

    private void OnCollisionEnter(Collision collision) // 충돌했다면
    {
        // 총알 Destroy 가 아닌 Disable
        DisableBullet();        
    }

    void DisableBullet()
    {
        gameObject.SetActive(false);
    }
}

using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;

public class FireLogic : MonoBehaviour
{
    [Header("발사 관련 설정")]
    [SerializeField] GameObject bulletPrefab;
    [SerializeField] int initialPoolSize = 10;
    [SerializeField] float bulletSpeed = 20f;

    private Queue<GameObject> bulletPool = new Queue<GameObject>();
    private InputAction fireAction;


    //▼총알 새로 만들고, 이걸 풀에 담는다
    GameObject CreateNewBullet()
    {
        GameObject newBullet = Instantiate(bulletPrefab); //그냥 만든거
        newBullet.SetActive(false); //만들자마자 꺼버리기
        bulletPool.Enqueue(newBullet);
        return newBullet;
    }

    void OnShoot(InputAction.CallbackContext ctx)////////////////
    {
        FireBullet();
    }
    private void Awake()
    {
        for (int i = 0; i < initialPoolSize; i++)
        {
            CreateNewBullet();
        }
        fireAction = InputSystem.actions["Attack"]; //////////////////
    }

    private void OnEnable()
    {
        fireAction.performed += OnShoot;//////////////
    }

    private void OnDisable()
    {
        fireAction.performed -= OnShoot;////////////////
    }

    void FireBullet()
    {
        GameObject bullet = GetInactiveBullet();
        if (bullet == null)
        {
            //return; 게임 성향에 맞게끔 위 아래 선택
            bullet = CreateNewBullet();
        }

        bullet.transform.position = transform.position + transform.forward * 2; //발사자 포지션 살짝 앞에
        bullet.transform.rotation = transform.rotation; //발사자의 로테이션
        bullet.SetActive(true); //풀에서 사용중인건 켜주기

        bullet.GetComponent<BulletCtrl>().SetOwner(this);

        Rigidbody rb = bullet.GetComponent<Rigidbody>();
        rb.linearVelocity = transform.forward * bulletSpeed;
    }
    GameObject GetInactiveBullet()
    {
        if (bulletPool.Count > 0) //사용할 수 있는 총알이 있다는 뜻
        {
            return bulletPool.Dequeue();
        }
        return null;
    }
    public void ReturnBullet(GameObject bullet)
    {
        bullet.SetActive(false);
        bulletPool.Enqueue(bullet);
    }
}
using Unity.VisualScripting;
using UnityEngine;

public class BulletCtrl : MonoBehaviour
{
    public float lifeTime = 3f;
    Rigidbody rb;

    //추상적인 개념을 하나 들고있게 할 것. 풀을 가질 수 있는 객체
    FireLogic fireLogic; //파이어로직이 무언가를 상속받고, 상속을 준 부모 객체에는 풀이 있어야 한다

    public void SetOwner(FireLogic fire)
    {
        fireLogic = fire;
    }

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }
    private void OnEnable()
    {
        Invoke(nameof(DisableBullet), lifeTime);
    }
    private void OnDisable()
    {
        rb.linearVelocity = Vector3.zero; //만약 이전 속도가 남아있는 버그가 있다면 여기 체크
        rb.angularVelocity = Vector3.zero;
        CancelInvoke(); //모든 인보크 없애버림
    }
    private void OnBecameInvisible()
    {
        DisableBullet();
    }

    private void OnCollisionEnter(Collision collision)
    {
        DisableBullet();
    }

    void DisableBullet()
    {
        fireLogic.ReturnBullet(this.gameObject);
    }
}

 

▼ 오브젝트 풀 심화

using UnityEngine;
using System.Collections.Generic;

public class ObjPool<T> where T : MonoBehaviour
{
    Queue<T> pool; // 여기에 몬스터 , 적 , 파티클 등등
    private T prefab;

    public ObjPool(T prefab, int initSize)
    {
        this.prefab = prefab;
        pool = new Queue<T>();

        for(int i = 0; i < initSize; i++)
        {
            T Instance = Object.Instantiate(prefab);
            Instance.gameObject.SetActive(false);
            pool.Enqueue(Instance);
        }
    }

    public T GetObject()
    {
        if(pool.Count > 0)
        {
            T obj = pool.Dequeue();
            obj.gameObject.SetActive(true);
            return obj;
        }

        // 만약 풀을 늘릴 수 있는 계열의 게임이었다면?

        T newObj = Object.Instantiate(prefab);
        return newObj;
    }

    public void ReturnObject(T obj)
    {
        pool.Enqueue(obj);
        obj.gameObject.SetActive(false);
    }
}

 

▼ 유니티에 내장된 오브젝트 풀 기능 사용하기

using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Pool;

public class RealFinalPool : MonoBehaviour
{
    #region 람다식
    // 람다식 : (입력값) => {함수의 동작}
    //int GetNumber()
    //{
    //    return 35;
    //}
    // () => 35;
    #endregion

    ObjectPool<GameObject> myPool; // 오브젝트 내장 풀 이미 만들었음
    public GameObject toInstantiate; // 풀이 사용할 프리팹

    private void Start()
    {
        myPool = new ObjectPool<GameObject>
            (
                CreateNewObject, // 생성할 때
                ActivateObject, // GET 을 수행할 것
                DeactivateObject, // SET 을 수행할 것
                DestroyObject // 파괴할 때
            );
    }

    // 객체를 생성하는 메서드
    GameObject CreateNewObject()
    {
        return Instantiate(toInstantiate); // 프리팹을 인스턴스화 시키고 , 방금 만들어진거 반환
    }

    void ActivateObject(GameObject obj) // 객체를 활성화하는 메서드 및 풀에서 꺼낼 때 수행해야 할 메서드 정
    {
        obj.SetActive(true); // 우치를 발사자 위치로 바꾼다던지..
    }

    void DeactivateObject(GameObject obj)
    {
        obj.SetActive(false); // 이외에도 리니어벨로시티 0 으로 만드는 등등
    }

    void DestroyObject(GameObject obj)
    {
        Destroy(obj.gameObject); // 이외에도 스코어를 올리거나 , 혹은 씬 전환 등
    }

    // 플레이어는 아래처럼 플레이어 로직만 들고 있게 하고 싶다
    private void Update()
    {
        if(Input.GetKeyUp(KeyCode.Escape))
        {
            var ibj = myPool.Get();
            ibj.GetComponent<Rigidbody>().AddForce(transform.position);
        }
    }
}

 

▼ 람다식을 사용해서 코드 간결하게 하기

using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Pool;

public class RealFinalPool : MonoBehaviour
{
    ObjectPool<GameObject> myPool; // 오브젝트 내장 풀 이미 만들었음
    public GameObject toInstantiate; // 풀이 사용할 프리팹

    private void Start()
    {
        myPool = new ObjectPool<GameObject>
            (
            // 명명된 인수
                createFunc: () => Instantiate(toInstantiate), // CreateFunc ( toInstantiate 캡쳐 )
                actionOnGet: obj => obj.SetActive(true),  // OnGet
                actionOnRelease: obj => obj.SetActive(false), // OnRelease
                actionOnDestroy: obj => Destroy(obj.gameObject) // 파괴할 때
            );
    }

    // 플레이어는 아래처럼 플레이어 로직만 들고 있게 하고 싶다
    private void Update()
    {
        if(Input.GetKeyUp(KeyCode.Escape))
        {
            var ibj = myPool.Get();
            ibj.GetComponent<Rigidbody>().AddForce(transform.position);

            myPool.Release(this.gameObject); // 반환시키고자 하는 총알
        }
    }
}

 

'📖TIL' 카테고리의 다른 글

251112 Particle  (0) 2025.11.12
251112 AudioSource  (0) 2025.11.12
251111  (0) 2025.11.11
251110  (0) 2025.11.10
251109  (0) 2025.11.09
'📖TIL' 카테고리의 다른 글
  • 251112 Particle
  • 251112 AudioSource
  • 251111
  • 251110
DevHoChan
DevHoChan
맨땅에서 시작하는 코딩 도전
  • DevHoChan
    Debugging Life
    DevHoChan
  • 전체
    오늘
    어제
    • 분류 전체보기 (374)
      • 🕹️Game Life (1)
      • 🖥️Computer Science (5)
      • 📖TIL (141)
        • 🔥Projects (16)
        • 💡DevTips (5)
        • 🤔발생한 문제와 해결 (5)
        • 🔮Unity Graphics (5)
        • 🎤Interview (3)
        • ✅CodingTest (9)
      • 🚀Game Release (4)
      • 🧊Unity Basic (58)
        • 📌용어 사전 (1)
        • 에디터&인터페이스 (3)
        • 디버그 (1)
        • 라이프사이클 (4)
        • 게임오브젝트 (4)
        • 프리팹 (1)
        • 오브젝트풀링 (4)
        • 애트리뷰트 (2)
        • 트랜스폼 (4)
        • 물리&충돌 (1)
        • 프레임&델타타임 (4)
        • 코루틴&이벤트 (7)
        • 수학&보정함수 (3)
        • 디자인패턴 (9)
        • UGUI (3)
        • 벡터 ( Vector ) (3)
        • 씬 ( Scene ) (2)
        • 데이터 관리 (2)
      • ⭐C Sharp (99)
        • 📌용어 사전 (1)
        • 📌문법 사전 (6)
        • 메모리 관리 (3)
        • 00. 문법 (17)
        • 01. 변수 (3)
        • 02. 자료형 (2)
        • 03. 연산자 (6)
        • 04. 조건문 (2)
        • 05. 반복문 (2)
        • 06. 배열 (3)
        • 07. 메서드(함수) (7)
        • 08. 열거형 (3)
        • 09. 구조체 (2)
        • 10. 참조 (2)
        • 11. 객체 지향 (11)
        • 12. 델리게이트 (3)
        • 13. 디자인 패턴 (7)
        • 14. LINQ (1)
        • 📂▼자료구조 (2)
        • 15-1. 제네릭 (3)
        • 15-2. 배열 (4)
        • 15-3. 리스트 (2)
        • 15-4. 스택과 큐 (2)
        • 15-5. 딕셔너리 해시테이블 (2)
        • 15-6. 트리와 그래프 (3)
      • 📊Algorithm (16)
        • BigO (2)
        • 정렬 (4)
        • 셔플 (2)
        • 탐색 (6)
        • 최적화 (1)
      • 📝Game Design (16)
      • 🤖​AI Tools (12)
        • AI 리뷰 분석 (6)
        • Player2 (0)
        • 3D 모델링 (1)
        • 2D 스프라이트 (0)
        • 이미지 (2)
        • 사운드 (1)
        • 동영상 (1)
        • 문서 (1)
      • 🌍Network (6)
      • 🌱Github (11)
        • 기본 개념 (7)
        • 명령어 (1)
        • 도구 활용 (1)
      • ⚙️Visual Studio (5)
        • 🔧설치 및 환경설정 (2)
        • ⌨️HotKey (1)
        • 🚨디버깅 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    부트캠프
    algorithm
    til
    유니티
    게임기획
    기획
    디자인패턴
    unity
    csharp
    객체지향
    GitHub
    자료구조
    자료형
    CodingTest
    문법
    OOP
    메모리관리
    c#
    게임디자인
    gamedesign
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
DevHoChan
251112 Object Pool
상단으로

티스토리툴바