Unity--对象池

对象池解决的问题:1.内存碎片化.,2.减少实例化和销毁带来的开销,3.减少GC回收机制  

应用场景:在unity中要知道创建游戏对象和销毁对象都是需要占用资源的,如果当你需要频繁创建和销毁对象的时候(比如英雄联盟的小兵,如果一局游戏时间很长的话,就会一直创建小兵和销毁小兵,就会造成资源的浪费),这个时候就需要引入对象池的概念,比如吃饭用筷子,如果你家用一次性筷子,那么每一次吃饭就需要去买一次性筷子(创建对象),买筷子需要花钱(消耗资源),吃完后就会把一次性筷子扔了(销毁对象),这样就会浪费钱(浪费资源)。对象池可以把它想象成你家吃饭的筷子篓,筷子篓(对象池)里面存在一些筷子(对象),在需要的时候就从筷子篓里面拿筷子(从对象池里面取出对象),吃完了需要清洗筷子(对象数据还原),洗完后有放回到筷子篓(将对象放回筷子篓),这样就不会造成资源的消耗

举个例子:创建,销毁的方式创建出很多球,可以用Instantiate()方法创作出球,然后在球上面挂载 Destroy(gameObject,3f)函数,就可以达到创建球三秒后自动销毁的效果,因为比较简单这里就不具体展示代码

大概效果就是这样

 接下来使用对象池,一般对象池需要有这几个方法,1.获取一个对象,2.回收一个对象,3.初始化方法(预先生成一些对象供使用),第3个方法也可以不一定要写,等到需要使用对象池的时候再往对象池里面添加对象也可以

这里就大概需要三个脚本,一个对象脚本,一个对象池的脚本,一个创建对象放在对象池的脚本

对象池脚本:

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

public class ObjectPool : MonoBehaviour
{
   public GameObject Object;//首先要知道对象池里面存放的对象
   public int ObjecNumber = 100;//初始化对象数量
   private Queue<GameObject> pool = new Queue<GameObject>();//用队列来做对象池,也可以用list
    public Transform PoolTransform;//对象池的位置,用来存放对象
   
   public static ObjectPool instance {  get; private set; }//使用单例模式来调用对象池
    private void Awake()
    {
        instance = this;
    }
    private void Start()
    {
        InitPool();
    }

    //初始化对象池,再对象池中存放一定数量的对象
    void InitPool()
    {
        GameObject Temp;
        for(int i=0; i< ObjecNumber; i++)
        {
            Temp= Instantiate(Object, PoolTransform);//生成一个对象
            pool.Enqueue(Temp);//将对象放在对象池中
            Temp.SetActive(false);
        }
    }

    //对象池,对外提供的方法
    //获取一个对象
    public GameObject GetObject()
    {
        GameObject temp;
        if(pool!=null&&pool.Count>0)//这里是判断对象池存在且对象池里面有对象
        {
            temp= pool.Dequeue();
            temp.SetActive(true);
            return temp;
        }
        else//如果池子里面没有对象了
        {
            temp = Instantiate(Object);
            return temp;
        }
    }

    //放回对象,将对象放入对象池中
    public void ReturnObject(GameObject Object)
    {
        Object.SetActive(false);
        pool.Enqueue(Object);//入队
    }
}

创建对象放在对象池的脚本:

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

public class CreatObjectAtPool : MonoBehaviour
{   
    void Update()
    {
        GameObject Object = ObjectPool.instance.GetObject();//从对象池中取出
    }
}

对象脚本

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

public class ObjectAtPool : MonoBehaviour
{
    private void OnEnable()//!!主要这里一定要在OnEnable的声明周期函数中调用放回对象池的操作,因为控制对象是靠对象的激活与失活
                            //如果在start中就只会在创建的第一次才会调用延时后再放回,当第二次激活后就不会再调用了
    {
        StartCoroutine(HideObject());
    }
    private void OnDisable()
    {
        //这一步是清理对象数据的工作,放在对象失活函数中,那就是还原数据的状态,这里就把对象的位置还原
        this.gameObject.transform.position=ObjectPool.instance.PoolTransform.position;
    }

    IEnumerator HideObject()//隐藏对象
    {
        yield return new WaitForSeconds(3);
        ObjectPool.instance.ReturnObject(this.gameObject);//将对象放回对象池
    }
    private void Start()
    {
        
        if (this.gameObject.transform.parent==null) //这一步是为了销毁超出对象池,通过实例化创建出来的对象
        { 
            Destroy(gameObject,3f);
        }
    }
}

这是使用自定义的对象池,在unity2021以上的版本中,有自带的对象池功能,引入命名空间using UnityEngine.Pool;即可使用,同样,使用unity自带的对象池也最好需要创键三个脚本,一个对象脚本,一个对象池脚本,一个生成对象脚本

这里重点介绍对象池脚本

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

public class UnityObjectPool : MonoBehaviour
{
   public ObjectPool<GameObject> pool;//使用unity自带的对象池
   public GameObject ObjectAtUnityPool;//对象池中的对象
   public Transform UnityObjectPoolTrans;

    bool ReturnCheck=false;
    private int defaultCapacity = 10;
    private int maxSize = 1000;
    public static UnityObjectPool instance {  get; private set; }
    private void Awake()
    {
        instance = this;
    }
    // Start is called before the first frame update
    void Start()
    {
        CreatPool();
    }   
    void CreatPool()//创建对象池
    {
        //CreatObject是一个委托,它与defaultCapacity就会一起初始化对象池,它往对象池创造对象时调用的方法
        //GetObject是一个委托,从对象池获取对象
        //ReturnObject是一个委托,将对象放回对象池
        //DestroyExtraObject是一个委托,这是方法是如果创造的对象超出了对象池的最大容量,应该怎么销毁额外的对象
        //ReturnCheck是一个布尔变量,返回放回对象时,true就会对对象池中是否已近存在该对象进行检测,避免重复放回
        //defaultCapacity默认容量,初始对象池中对象的个数
        //maxSize最大容量
        pool = new ObjectPool<GameObject>(CreatObject, GetObject, ReturnObject, DestroyExtraObject, ReturnCheck, defaultCapacity, maxSize);
    }

    GameObject CreatObject()//创建对象的函数
    {
        GameObject Object = Instantiate(ObjectAtUnityPool, UnityObjectPoolTrans);
        Object.transform.position = UnityObjectPoolTrans.position;
        return Object;
    }
    void GetObject(GameObject Object)//从对象池中取对象要执行的事就只需要将该对象设置为激活状态
    {
        Object.gameObject.SetActive(true);
    }
    void ReturnObject(GameObject Object)//将对象放回对象池
    {
        Object.gameObject.SetActive(false);
    }
    void DestroyExtraObject(GameObject Object) //销毁额外的对象
    {
        Destroy(Object, 3f);
    }

   
}

可以看见与自己写对象池最大的不同就是代码显得简洁,不用自己写出队入队操作,只需要写在

1.拿出对象池的时候需要做什么,

2.放回对象池的时候需要做什么,

3.创造对象的时候需要做什么和

4.销毁额外的对象的时候需要做什么还有一些参数即可,

不过需要注意,从对象池取对象的操作是pool.Get(),放回操作是pool.Release(GameObject Object);为什么取出来不需要参数,而放进去需要传入对象呢,因为取出来是从对象池里面直接拿出来,不需要告诉取谁(队列先进先出的思想,取排在前面的)所以不用传参数,但是放回去就需要知道到底要把哪一个对象放回到对象池里面。具体可以参考自己写的对象池的取对象方法

另外再附上

对象脚本

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

public class ObjetcAtUnityPool : MonoBehaviour
{
    private void OnEnable()
    {
        StartCoroutine(HideObject());
    }
    private void OnDisable()
    {
        //还原数据
        this.gameObject.transform.position = gameObject.transform.parent.position;
    }
    IEnumerator HideObject()
    {
        yield return new WaitForSeconds(3);
        UnityObjectPool.instance.pool.Release(this.gameObject);//放回对象池
    }
   
}

创造对象

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

public class CreatObjectAtUnityPool : MonoBehaviour
{
  
    // Update is called once per frame
    void Update()
    {
        UnityObjectPool.instance.pool.Get();//从对象池中取对象,,如果对象池中没有对象了就会先创建对象,再取出来
    }
}

等以后能够熟练使用对象池了再来提炼总结

猜你喜欢

转载自blog.csdn.net/qq_62947569/article/details/129812598
今日推荐