در بخش قبلی، از اینکه چرا از "Object Pool" استفاده میکنیم و چطور استفاده میکنیم، پرداخته شد. حالا میرسیم به بخش دوم:
نکته آغازین: از اونجایی که Pool همچنان مجموعه ای از آبجکت هارو همراه خودش داره، وقتی هم که باهاش کاری نداریم همچنان در حافظه باقی است و جلوی Garbage Collector رو میگیره. پس برای اینکه این اتفاق نیافته، هر موقع با Pool کاری نداشتیم، بهتره که تمامی اتصالات و Reference هایی که به Pool مربوط میشن رو از بین ببریم.
آیا ایجاد و حذف آبجکت ها، به CPU ربطی داره؟
قطعا همینطوره! فرایند Instantiate() و Destroy() برای انجام کارشون نیازمند یک کمک کوچیک از سمت CPU هستن.
حالا فکر کنید چندین آبجکت مرتب درخواست Instantiate() و Destroy() رو ارسال کنن. فاجعه رخ میده.
پس فرایند Object Pooling، هوای CPU رو هم داره!
object Pool در یک نگاه:
0. قبل از هر چیز، باید یک فایل C# در یونیتی ایجاد کنیم :)
using UnityEngine; using System.Collections.Generic; public class Object_Pooler : MonoBehaviour { }
1. چندتا متغییر کوچیک رو باید وارد کنیم:
public class Object_Pooler : MonoBehaviour { //آبجکتی که قصد داریم ازش در بازی زیاد استفاده کنیم public GameObject obj; //حد و مرز برای ایجاد آبجکت public int amount = 20; //آبجکت هایی که در ابتدای بازی ساخته میشن، داخل این لیست ذخیره و استفاده میشن public static List<GameObject> pooledObjects;}
2. حالا در نقطه استارت بازی، دستور میدیم که تمام آبجکت هارو ایجاد کن و در حالت "استراحت (غیرفعال)" قرار بده:
public class Object_Pooler : MonoBehaviour { //آبجکتی که قصد داریم ازش در بازی زیاد استفاده کنیم public GameObject _obj; //حد و مرز برای ایجاد آبجکت public int _amount = 20; //آبجکت هایی که در ابتدای بازی ساخته میشن، داخل این لیست ذخیره و استفاده میشن public static List<GameObject> _pooledObjects; void Start() { _pooledObjects = new List<GameObject>(); for (int i=0; i < _amount; i++) { // ایجاد اولیه GameObject new_obj = (GameObject)Instantiate(_obj); // آبجکت رو روی حالت "استراحت(غیرفعال)" میگذاریم new_obj.SetActive(false); // آبجکتی که ایجاد کردیم رو به لیست اضافه میکنیم _pooledObjects.Add(new_obj); } } }
3. یک تابع برای استفاده از آبجکت های "درحال استراحت"، اضافه میکنیم:
public class Object_Pooler : MonoBehaviour { public static GameObject GetPooledObject() { for (int i=0; i < _pooledObjects.Count; i++) { //چک میکنیم که آبجکت مورد نظر غیرفعال باشد if (!_pooledObjects[i].activeInHierarchy) return _pooledObjects[i]; } return null; } }
4. حالا در یک اسکریپت دیگر، میتوانیم از Object Pool استفاده کنیم:
public class Gun : MonoBehaviour { void Update() { if (Input.GetMouseButtonDown(0)) Shoot(); } void Shoot() { // آبجکتی که در لیست وجود دارد و درحال استراحت است GameObject obj = Object_Pooler.GetPooledObject(); if (obj != null) { obj.transfrom.position = Vector3.zero; //فعالسازی obj.SetActive(true); } } }
5. در آخر، به آبجکتی که از Gun شلیک میشه، میگیم بعد از فلان کار انجام شده، غیرفعال شود:
مثلا بعد از 5 ثانیه، یا بعد از برخورد و...
توی این مثال، برخورد رو در نظر گرفتم:
public class Bullet : MonoBehaviour { private void OnTriggerEnter(Collider other) { if (other.tag == "Player") gameObject.SetActive(false); } }
توی این مثال، زمان رو در نظر گرفتم:
public class Bullet : MonoBehaviour { void OnEnable() { Invoke("Destroy", 5f); } private void Destroy() { gameObject.SetActive(false); } private void OnDisable() { CancelInvoke(); } }
مبحث Object Pool یکی از مباحث مهم بهینه سازی بازی است که امیدوارم به پیاده سازی بهتر اون کمک کرده باشم.