[Unity Optimize] Use Object Pooling (Object Pooling) to optimize the project

1 Introduction to Object Pooling

Object Pooling in Unity is an optimization scheme for improving game performance and reducing resource waste. Especially when you need to quickly create and destroy game objects, such as when firing bullets in some shooting games.

The object pool pre-creates a certain number of objects before the game runs , stores them in a reuse dictionary (or array), and only activates or deactivates the required game objects when needed. In fact, it just recycles objects instead of using The native Instantiate and Destroy methods create and destroy objects . We call this dictionary (or array) of all active and inactive objects a pool. This can greatly reduce the overhead of frequently creating and destroying objects, and can reduce memory allocation and garbage collection, and improve the running efficiency of the game.

2 Implement the object pool script

Implement an object pool script ObjectPool.cs, using serialized game object prefabs to quickly generate and recycle a large number of objects of the same type.

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 此类实现了对象池模式,用于创建和管理大量的游戏对象
/// </summary>
public class ObjectPool
{
    
    
    private readonly GameObject prefab; // 预制体对象,用来创建新的游戏对象
    private readonly int initialPoolSize; // 初始对象池大小,即预先生成的游戏对象数量
    private readonly bool canGrow; // 是否可以动态扩展对象池大小
    private readonly List<GameObject> pool = new List<GameObject>(); // 游戏对象池

    // 单例对象,用于全局访问对象池
    public static ObjectPool Instance;

    // 私有无参构造函数,避免从外部创建对象池实例
    private ObjectPool()
    {
    
    
        this.prefab = null;
        this.initialPoolSize = 0;
        this.canGrow = false;
    }

    // 构造函数,创建对象池实例,并初始化预制体对象、对象池大小、是否可扩展等参数
    public ObjectPool(GameObject prefab, int initialPoolSize, bool canGrow)
    {
    
    
        this.prefab = prefab;
        this.initialPoolSize = initialPoolSize;
        this.canGrow = canGrow;

        InitializePool(); // 初始化对象池
    }

    // 从对象池中获取未被使用的游戏对象,如果对象池已满并且可以扩展,将会创建新的游戏对象
    public GameObject GetObject()
    {
    
    
        GameObject obj = pool.Find(o => !o.activeSelf); // 查找未被激活的游戏对象
        if (obj == null && canGrow) // 如果对象池满了并且可以扩展
        {
    
    
            obj = AddObjectToPool(); // 创建新的游戏对象
        }

        if (obj != null)
        {
    
    
            obj.SetActive(true); // 激活游戏对象
        }

        return obj;
    }

    // 将游戏对象返回到对象池中
    public void ReturnObject(GameObject obj)
    {
    
    
        obj.SetActive(false); // 取消游戏对象的激活状态
    }

    // 初始化对象池,创建一定数量的游戏对象并加入到游戏对象池中
    private void InitializePool()
    {
    
    
        for (int i = 0; i < initialPoolSize; i++)
        {
    
    
            GameObject obj = GameObject.Instantiate(prefab); // 创建新的游戏对象
            obj.SetActive(false); // 初始时将游戏对象设置为未激活状态
            pool.Add(obj); // 将游戏对象加入到对象池中
        }
    }

    // 向对象池中添加新的游戏对象
    private GameObject AddObjectToPool()
    {
    
    
        GameObject obj = GameObject.Instantiate(prefab); // 创建新的游戏对象
        obj.SetActive(false); // 初始时将游戏对象设置为未激活状态
        pool.Add(obj); // 将游戏对象加入到对象池中
        return obj;
    }
}

3 Use object pool to generate Cube

Using the above object pool, write the script NewCube.cs to generate a Cube at the position where the left mouse button is clicked, click the right mouse button to destroy the previously generated Cubes one by one, and press the X key on the keyboard to destroy all the cubes.

using System.Collections.Generic;
using UnityEngine;

public class NewCube : MonoBehaviour
{
    
    
    [SerializeField]
    private GameObject cubePrefab; // Cube预制体
    [SerializeField]
    private float spawnHeight = 10f; // 在鼠标落下的位置生成Cube时使用的高度值
    [SerializeField] 
    private int initialPoolSize = 20; // 初始化对象池时的初始大小
    [SerializeField] 
    private bool canGrow = true; // 当对象池中的对象数量达到最大值时,canGrow 为 true 的话会增加对象池的大小

    private GameObject currentCube; // 当前的Cube游戏对象
    private Vector3 lastCubePosition; // 上一个Cube的位置
    private List<GameObject> cubesList = new List<GameObject>(); // 用于记录所有的Cube
    

    private void Start()
    {
    
    
        ObjectPool.Instance = new ObjectPool(cubePrefab, initialPoolSize, canGrow);
    }

    private void Update()
    {
    
    
        if (Input.GetMouseButtonDown(0)) // 鼠标左键按下
        {
    
    
            SpawnCube();
        }
        else if (Input.GetMouseButtonDown(1)) // 鼠标右键按下
        {
    
    
            DeleteLastCube();
        }

        if (Input.GetKeyDown(KeyCode.X)) // X键按下
        {
    
    
            ClearCubes();
        }
    }

    //生成Cube对象
    private void SpawnCube()
    {
    
    
        Vector3 mousePos = Input.mousePosition;
        mousePos.z = spawnHeight;
        Vector3 spawnPos = Camera.main.ScreenToWorldPoint(mousePos);

        if (currentCube != null)
        {
    
    
            lastCubePosition = currentCube.transform.position;
        }

        currentCube = ObjectPool.Instance.GetObject();
        currentCube.transform.position = spawnPos;
        currentCube.SetActive(true);

        Rigidbody rb = currentCube.GetComponent<Rigidbody>();
        rb.velocity = Vector3.zero;
        rb.useGravity = true;

        lastCubePosition = currentCube.transform.position;

        cubesList.Add(currentCube); // 将当前Cube添加到列表中
    }

    // 删除上一个生成的 Cube 对象
    private void DeleteLastCube()
    {
    
    
        if (cubesList.Count > 0)
        {
    
    
            ObjectPool.Instance.ReturnObject(cubesList[cubesList.Count - 1]);
            cubesList.RemoveAt(cubesList.Count - 1); // 从列表中删除最后一个Cube
        }

        if (cubesList.Count > 0)
        {
    
    
            lastCubePosition = cubesList[cubesList.Count - 1].transform.position;
        }
        else
        {
    
    
            lastCubePosition = Vector3.zero;
        }
    }

    // 销毁所有的Cube对象
    private void ClearCubes()
    {
    
    
        int childCount = cubesList.Count;

        for (int i = childCount - 1; i >= 0; i--)
        {
    
    
            GameObject obj = cubesList[i].gameObject;
            ObjectPool.Instance.ReturnObject(obj);
        }

        currentCube = null;
    }
}

4 Effect display

First, create a new plane at the initial position (0,0,0) in Unity, and appropriately increase the X and Z values ​​of its Scale; then, create a new Cube object, add a Rigidbody component (gravity effect) to it and Make it into a prefab; create an empty object in the scene, mount the NewCube script, and drag the completed Cube prefab to its CubePrefab parameter.

insert image description here

Video demo:

5 Object Pool Plugin for Unity Asset Store

The packaged object pool plug-in in the Unity resource store: Lean Pool
can also directly use the built-in object pool API of Unity , and use the following namespace to import:

using UnityEngine.Pool;

Guess you like

Origin blog.csdn.net/qq_41084756/article/details/130656362