Unity对象池的简单理解

什么是对象池?

  通常我们需要用到某个游戏对象时,我们会在内存中分配一部分空间new一个对象出来,之后在该对象完成任务之后,再将其摧毁,释放掉内存。在Unity中就是我们在需要的时候调用Instantiate()函数来生成一个游戏对象。

	GameObject gameObject = GameObject.Instantiate(prefab);

  在用完该对象之后,调用Destory()函数将其摧毁。

	Destoty(gameobject);

  Unity引擎的自动内存管理系统会帮我们完成垃圾回收(GC)的工作,但垃圾回收本身也是要消耗性能的,如果对象过多就会十分消耗性能,如果我们要频繁地生成与摧毁对象,GC就会非常忙,导致游戏卡顿甚至崩溃,因此我们应该尽可能少的触发垃圾回收机制,这就要用到对象池了。

对象池的原理:

  在游戏初始化的阶段,先取出一块完整的内存空间,用来生成运行过程中需要用到的对象,让它们预备好随时被启用。在我们需要用到某个对象的时候,我们不再new新的对象,而是从预备好的这块内存空间中,即对象池中取出来该对象使用,用完之后再将该对象放回对象池,随取随用。
在这里插入图片描述

使用对象池与不使用对象池对比:

使用对象池与不使用对象池对比
  很明显,使用对象池相比于不使用对象池会给游戏带来显著的性能提升,尤其是对于像是射击类游戏这种需要创建大量的子弹对象的游戏,使用对象池是必不可少的,同时使用对象池还能够减少目标游戏平台的性能消耗,增加游戏运行流畅度。

创建对象池:

  这里我们用队列(Queue)的数据结构来存储我们要实例化的游戏对象,即我们做好的预制体(Prefabs)。

	Queue<GameObject> queue;
创建对象池的步骤:

在这里插入图片描述
  (1)生成备用对象,禁用它们,准备随时被启用,将它们挂载到同一个父对象上,将对象初始化。

	//复制预制体对象 加入对象池
	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)从池中取出可用对象,将其出队,若池中没有可用对象,则调用生成备用对象函数。

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

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

  (3)启用可用对象,可以根据自己的实际要求写几个重载,让启用对象做出某些选择。

	//启用可用的对象
    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)让完成任务的对象返回对象池。

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

简单对象池完整代码:

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);
    }
}

  以上是我对实现对象池的理解,写为博客,作为自己的学习记录与参考,欢迎指正。

猜你喜欢

转载自blog.csdn.net/weixin_44740741/article/details/129858524