对象池的简单用法

参考:
1.Unity —– 对象池GameObjectPool
2.Unity3D内存管理——对象池(Object Pool)
游戏开发中需要经常实例化和访问IO来动态加载和调用资源,频繁的访问和调用IO组件是一个比较消耗资源的操作,因此,为了提升游戏的体验效果,一般会使用对象池来预先存储需要大量实例化的对象和资源,当真正需要使用对象和资源的时候,不是调用IO组件去实例化资源,而是从对象池中将预先实例化好的对象取出来使用,用完之后,当需要将对象销毁的时候,并不是调用Destroy()方法将对象直接销毁,而是将对象的状态设置Seactive(false),并把对象重新放入池中,再次使用的时候,对对象进行初始化(或者可以在放入对象池的时候)。对象池虽然很好用,但是由于对象池会预先实例化一些对象存储在其中,会占据一定的存储空间,因此,也需要注意该什么时候建立、使用、释放对象池。说了这么多,具体看代码分析。
首先创建一个对象池,在对象池中预先生成需要实例化的对象,为了对象池能被全局访问,将对象池定义为一个单例模式:

    private static MyObjectPool _instance;//创建一个单例模式
    public static MyObjectPool Instance
    {
        get { return _instance; }
    }
    void Start()
    {
        _instance = this;
    }


声明两个字典类型的集合:

//用于存储需要实例化的对象,以及实例化后对象的集合
Dictionary<GameObject, List<GameObject>> pooledObjects = 
        new Dictionary<GameObject,List<GameObject>();

Dictionary<GameObject, GameObject> spawnedObjects = 
        new Dictionary<GameObject, GameObject>();


声明一个数据配置类,用于保存需要实例化的对象以及需要实例化的个数:

//不继承自Monobehavior的类,需要将类型序列化
//PoolConfig类用来配置需要实例化的对象的预制和需要生成的数量
 [Serializable]
 public class PoolConfig
    {
        public int count;//实例化对象的数量

        public GameObject prefab;//需要实例化的对象的预制
    }
 public PoolConfig[] poolConfigs;//可在Inspector中配置实例化的对象和数量


创建对象池,并向对象池中预先存储需要实例化的对象,在合适的时候调用CreatePool方法,可以放在场景的入口,或者通过某个函数触发(因为创建对象池会占用一定的存储空间,所以选择合适的建池时机是非常有必要的),这里做测试,我将对象池的创建放大了Start函数中。

void Start()
    {
        _instance = this;
        //根据配置文件,初始化池子,并创建对象,根据PoolConfig中的配置创建
        for (int i = 0; i < this.poolconfigs.Length; i++)
        {
            CreatePool(this.poolconfigs[i].prefab,this.poolconfigs[i].count);
        }
     }


创建和生成对象池,并向对象池中添加需要实例化的对象

public static void CreatePool(GameObject prefab, int initialPoolSize)
    {
    //是否预制为空,且池中不包含任何对象
        if (prefab != null && !Instance.pooledObjects.ContainsKey(prefab))
        {
            var list = new List<GameObject>();
            Instance.pooledObjects.Add(prefab, list);
            if (initialPoolSize > 0)
            {
                bool active = prefab.activeSelf;
                Transform parent = Instance.transform;
                while (list.Count < initialPoolSize)
                {
                    var obj = GameObject.Instantiate(prefab);
                    obj.transform.parent = parent;
                    list.Add(obj);
                }
                prefab.SetActive(false);
            }
        }
    }



对象池已经创建好了,池子中也存放了一些需要实例化的对象了,接下来,我们就需要从池子中取对象了。

//从池子中取对象,并初始化这个对象的一些属性,比如:坐标、缩放、以及父对象等等
 public static GameObject Spawn(GameObject prefab, Transform parnet, Vector3 position, Quaternion rotation)
    {
        List<GameObject> list;
        Transform trans;
        GameObject obj;
        //判断池子中是否存在该对象,如果存在,则取出,不存在,则创建一个新的对象
        if (Instance.pooledObjects.TryGetValue(prefab, out list))
        {
            obj = null;
            if (list.Count > 0)
            {
                while (obj == null && list.Count > 0)
                {
                    obj = list[0];
                    list.RemoveAt(0);
                }
                if (obj != null)
                {
                    trans = obj.transform;
                    trans.parent = parnet;
                    trans.localPosition = position;
                    trans.localRotation = rotation;
                    obj.SetActive(true);
                    Instance.spawnedObjects.Add(obj, prefab);
                    return obj;
                }
            }
            obj = GameObject.Instantiate(prefab);
            trans = obj.transform;
            trans.parent = parnet;
            trans.localPosition = position;
            trans.localRotation = rotation;
            obj.SetActive(true);
            Instance.spawnedObjects.Add(obj, prefab);
            return obj;
        }
        else
        {
            obj = GameObject.Instantiate(prefab);
            trans = obj.GetComponent<Transform>();
            trans.parent = parnet;
            trans.localPosition = position;
            trans.localRotation = rotation;
            return obj;
        }


当我们不在需要使用刚才取出来的对象时,我们并不是调用Destroy()函数将对象销毁,其实调用Destroy()函数之后,系统并不会马上将内存返还给程序,大量的Destroy()会造成应用卡顿。所以,我们调用对象池,将对象回收,再次存放到对象池中,并将对象的状态设置为:SetActive(false);

//回收对象的时候,我们可能需要做一些数据初始化的操作
//我们想函数参数中创建一个委托,使得对象的初始化工作,可以在对象本身执行
public static void Recyle(GameObject obj, Action RecyleAction)
    {
        GameObject prefab;
        if (Instance.spawnedObjects.TryGetValue(obj, out prefab))
        {
           // 将对象从添加到池中,并从生成对象中移除
            Instance.pooledObjects[prefab].Add(obj);
            Instance.spawnedObjects.Remove(obj);
            obj.transform.parent = Instance.transform;
            //如果委托的定义不为空,则调用委托执行对象的初始化
            if (RecyleAction != null)
            {
                RecyleAction();
            }
        }
        else
        {
            Debug.Log("没有找到,要把对象销毁");
            Destroy(obj);
        }
    }


到这里一个对象池需要具备的基本功能差不多就完成了,下面,分析一下使用对象池创建、回收对象的方法。
一、调用对象池实例化对象

//脚本用于从对象池中取对象,将脚本挂载到需要实例化对象的地方
MyObjectPool.Spawn(this.cube, this.transform, Vector3.zero, Quaternion.identity);


二、当资源需要释放时,调用回收函数

//声明一个委托,在对象呗回收时调用,主要执行一些对象初始化的工作
 private Action recyleAction;

 void OnEnable ()
    {
        StartCoroutine("RecyleObject");
        this.recyleAction = Recyle;

    IEnumerator RecyleObject()
    {
        yield return new WaitForSeconds(2);
        //当对象被实例化两秒之后回收对象,具体可以按照项目需求来自己定制
        MyObjectPool.Recyle(this.gameObject,this.Recyle);        
    }
     void Recyle()
    {
        this.GetComponent<Rigidbody>().velocity=Vector3.one;
        this.GetComponent<Rigidbody>().angularVelocity=Vector3.zero;
        this.gameObject.SetActive(false);
        recyleAction=null;
        Debug.Log("开始回收了");
    }
————————————————
版权声明:本文为CSDN博主「Sun.ME」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/JianZuoGuang/article/details/79040577


原文链接:https://blog.csdn.net/JianZuoGuang/article/details/79040577

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值