Object Pooling ile Ateş Etme Mekaniği

Oyun yapımında özellikle ateş etme, düşman oluşturma mekaniklerinde sık sık Instantiate() ve Destroy() metotları kullanılır. Ancak oluşturulan objelerin sahnede varlığı bile çok kalıcı değildir. Özellikle bu bir mermiye ait ise, neredeyse saniyeler içerisinde yeniden yok edilmesi gerekir. Bu da sürekli obje oluşturup, yok etme gereksinimini doğurur. Gameplay içerisinde her obje için Instantiate() ve Destory() metotlarını…

Oyun yapımında özellikle ateş etme, düşman oluşturma mekaniklerinde sık sık Instantiate() ve Destroy() metotları kullanılır. Ancak oluşturulan objelerin sahnede varlığı bile çok kalıcı değildir. Özellikle bu bir mermiye ait ise, neredeyse saniyeler içerisinde yeniden yok edilmesi gerekir. Bu da sürekli obje oluşturup, yok etme gereksinimini doğurur.

Gameplay içerisinde her obje için Instantiate() ve Destory() metotlarını kullanmak bir süreden sonra CPU’nun ne olduğunu şaşırmasına neden olur. 🙂

Object Pooling, gameplay’den önce ihtiyaç duyulan tüm objelerin oluşturulmasını sağlar. Bu şekilde sürekli obje oluşturup, yok ederken CPU’nun kasması önlenir. Bir obje havuzu şeklinde oluşturulduğu için genelde objeler Stack ya da List’lerin içerisinde oyun başlarken oluşturulur. Bu şekilde oluşturulan objeler daha sonra kullanılırken gameplay kısmında yeni obje üretilmediği için optimize edilmiş olur.

Object pooling temelinde bir optimizasyon sayılabilir.

Sürekli kullanılan ateş etme mekaniğinde ben de Object pooling tasarım desenini kullandım. Basit haliyle, aşağıdaki gibi bir Scene oluşturdum.

Bu ekranda statik bir oyuncumuz ve üzerinde kalabileceği bir üç boyutlu taban var. İki tane de material oluşturup yalnızca renk vermek için kullandım.

İkinci aşamada, bir mermi prefab’i oluşturdum ki; oluşturduğum prefab’i havuz için kullanabileyim.

Temel sahneyi tamamladıktan sonra ekrandan Bullet isimli prefab’i sildim ve kod kısmına geçtim.

İlk olarak Pool.cs adında bir sınıf oluşturdum. Bu benim en basit haliyle, objeleri tutacağım havuz ve oyun ilk çalıştırıldığında istediğim sayıda bu havuzu dolduracağım.

using System.Collections.Generic;
using UnityEngine;

public class Pool
{
    private GameObject _poolPrefab;
    private Stack<GameObject> pool = new Stack<GameObject>(); //havuzu Stack ile oluşturduk.

    public Pool(GameObject poolPrefab)
    {
        _poolPrefab = poolPrefab; 
    }

    public void FillPool(int limit)
    {
        for(int i = 0; i< limit; i++)
        {
            GameObject poolObject = Object.Instantiate(_poolPrefab);
            AddPool(poolObject);
        }
    }


    public void AddPool(GameObject poolObject)
    {
        poolObject.gameObject.SetActive(false);
        pool.Push(poolObject); //Stack'e objeyi atıyoruz.
    }

    public GameObject UnloadPool()
    {
        if(pool.Count > 0)
        {
            GameObject _object = pool.Pop();
            _object.gameObject.SetActive(true);
            return _object;
        }

        return Object.Instantiate(_poolPrefab);
    }
}

Pool’u oluşturduktan sonra, bir BulletTest sınıfı oluşturuyorum.

using System.Collections.Generic;
using UnityEngine;

public class BulletTest : MonoBehaviour
{
    public GameObject BulletPrefab; //oluşturduğum prefab'in atamasını Inspector'dan yapacağım.
    private Pool _pool;
    [SerializeField]
    private GameObject _turret;


    private void Start()
    {
        _pool = new Pool(BulletPrefab);
        _pool.FillPool(5); //Havuzu start kısmında dolduruyorum. 

    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            GameObject bulletObject = _pool.UnloadPool();
            bulletObject.gameObject.SetActive(true);

        }
    }

}

İlk aşamada yalnızca havuza eklediğimiz için şöyle bir sıkıntı oluşuyor; havuz boyutundan fazlaca oluşturmaya başlıyor.

Dört saniye sonra oluşturduğumuz prefab’i silmek için bir Coroutine oluşturdum.

IEnumerator BulletDestory(float min, GameObject _bullet)
    {
        yield return new WaitForSeconds(min);
        _pool.AddPool(_bullet);
    }

Bu bizim verdiğimiz float tipinden saniyeyi alıyor ve yok edilmesi gereken _bullet’ı da alıp daha sonra stack içerisinde deactive ediyor. Yeniden havuza gönderiyor.

Coroutine’i başlattığım yer ise şurası:

 if (Input.GetKeyDown(KeyCode.Space))
        {
            GameObject bulletObject = _pool.UnloadPool();
            bulletObject.gameObject.SetActive(true);
            StartCoroutine(BulletDestory(4f, bulletObject));
        }

Sonuç ekranı ise şu şekilde:

Proje kaynak kodları için Github: https://github.com/OzgeKocaoglu

 

KAYNAK: ÖZGE KOCAOĞLU