A simple understanding of Unity object pool

What is an object pool?

  Usually when we need to use a certain game object, we will allocate a new object in the memory and then destroy it to release the memory after the object completes its task. In Unity, we call the Instantiate() function to generate a game object when needed.

	GameObject gameObject = GameObject.Instantiate(prefab);

  After you are done with the object, call the Destory() function to destroy it.

	Destoty(gameobject);

  The automatic memory management system of the Unity engine will help us complete the work of garbage collection (GC), but garbage collection itself also consumes performance. If there are too many objects, it will consume very much performance. If we want to frequently generate and destroy objects, GC will It will be very busy, causing the game to freeze or even crash. Therefore, we should trigger the garbage collection mechanism as little as possible, which requires the use of object pools.

The principle of object pool:

  During the initialization stage of the game, a complete memory space is first taken out to generate objects needed during operation, so that they are ready to be activated at any time. When we need to use an object, we no longer create a new object, but take out the object from the prepared memory space, that is, the object pool, and put the object back after use. Object pool, ready to use.
Insert image description here

Comparison between using object pool and not using object pool:

Comparison between using object pool and not using object pool
  Obviously, using an object pool will bring significant performance improvements to the game compared to not using an object pool. Especially for games such as shooting games that require the creation of a large number of bullet objects, using an object pool is essential. At the same time, using the object pool can also reduce the performance consumption of the target game platform and increase the smoothness of the game.

Create an object pool:

  Here we use the queue data structure to store the game objects we want to instantiate, that is, the prefabs we have made.

	Queue<GameObject> queue;
Steps to create an object pool:

Insert image description here
  (1) Generate standby objects, disable them, prepare them to be enabled at any time, mount them to the same parent object, and initialize the object.

	//复制预制体对象 加入对象池
	GameObject Copy()
    {
    
    
        var copy = GameObject.Instantiate(prefab, parent);
        copy.SetActive(false);
        return copy;
    }
    //初始化对象池 将所有对象入队
    public void Init(Transform parent)
    {
    
    
        queue = new Queue<GameObject>();
        this.parent = parent;
        
        for (int i = 0; i < size; i++)
        {
    
    
            queue.Enqueue(Copy());
        }
    }

  (2) Take the available objects from the pool and dequeue them. If there are no available objects in the pool, call the function to generate spare objects.

    //从池中取出可用的对象
    GameObject AvailableObject()
    {
    
    
        GameObject avaliableObject = null;
        if (queue.Count > 0) //&& !queue.Peek().activeSelf
        {
    
    
            avaliableObject = queue.Dequeue();
        }
        else
        {
    
    
            avaliableObject = Copy();
        }

        //queue.Enqueue(avaliableObject);
        return avaliableObject;
    }

  (3) To enable available objects, you can write several overloads according to your actual requirements to let the enabled objects make certain choices.

	//启用可用的对象
    public GameObject PreparedObject()
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);

        return proparedObject;
    }

    //启用可用的对象 并让其出现在场景中的某个位置
    public GameObject PreparedObject(Vector3 position)
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);
        proparedObject.transform.position = position;

        return proparedObject;
    }

    //启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度
    public GameObject PreparedObject(Vector3 position, Quaternion rotation)
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);
        proparedObject.transform.position = position;
        proparedObject.transform.rotation = rotation;

        return proparedObject;
    }

    //启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度 并缩放其大小
    public GameObject PreparedObject(Vector3 position, Quaternion rotation, Vector3 localScale)
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);
        proparedObject.transform.position = position;
        proparedObject.transform.rotation = rotation;
        proparedObject.transform.position = localScale;

        return proparedObject;
    }

  (4) Let the object that completes the task return to the object pool.

	//让已用过的对象返回对象池(可与启用写在同一函数中)
    public void Return(GameObject gameObject)
    {
    
    
        queue.Enqueue(gameObject);
    }

Complete code of simple object pool:

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

[System.Serializable]
public class Pool
{
    
    
    public GameObject Prefab => prefab; 

    [SerializeField] GameObject prefab;
    //存储所有复制体的数量
    [SerializeField] int size = 1;

    //池的父对象
    Transform parent;

    Queue<GameObject> queue;

    //初始化对象池 将所有对象入队
    public void Init(Transform parent)
    {
    
    
        queue = new Queue<GameObject>();
        this.parent = parent;
        
        for (int i = 0; i < size; i++)
        {
    
    
            queue.Enqueue(Copy());
        }
    }

    //复制预制体对象 加入对象池
    GameObject Copy()
    {
    
    
        var copy = GameObject.Instantiate(prefab, parent);
        copy.SetActive(false);
        return copy;
    }

    //从池中取出可用的对象
    GameObject AvailableObject()
    {
    
    
        GameObject avaliableObject = null;
        if (queue.Count > 0) //&& !queue.Peek().activeSelf
        {
    
    
            avaliableObject = queue.Dequeue();
        }
        else
        {
    
    
            avaliableObject = Copy();
        }

        //queue.Enqueue(avaliableObject);
        return avaliableObject;
    }

    //启用可用的对象
    public GameObject PreparedObject()
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);

        return proparedObject;
    }

    //启用可用的对象 并让其出现在场景中的某个位置
    public GameObject PreparedObject(Vector3 position)
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);
        proparedObject.transform.position = position;

        return proparedObject;
    }

    //启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度
    public GameObject PreparedObject(Vector3 position, Quaternion rotation)
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);
        proparedObject.transform.position = position;
        proparedObject.transform.rotation = rotation;

        return proparedObject;
    }

    //启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度 并缩放其大小
    public GameObject PreparedObject(Vector3 position, Quaternion rotation, Vector3 localScale)
    {
    
    
        GameObject proparedObject = AvailableObject();

        proparedObject.SetActive(true);
        proparedObject.transform.position = position;
        proparedObject.transform.rotation = rotation;
        proparedObject.transform.position = localScale;

        return proparedObject;
    }

    //让已用过的对象返回对象池(可与启用写在同一函数中)
    public void Return(GameObject gameObject)
    {
    
    
        queue.Enqueue(gameObject);
    }
}

  The above is my understanding of implementing object pools. I wrote it as a blog as my own learning record and reference. Corrections are welcome.

Guess you like

Origin blog.csdn.net/weixin_44740741/article/details/129858524