Unity对象池简单应用

1.简单介绍

在Unity开发过程中,对象池是一种很好的能用于减少内存开销的方式。在许多场景中,比如角色射击出去的子弹,以及在游戏运行过程中频繁生成的敌人…如果在运行时实时在场景中Instance和Destory,在场景中需要实时生成的GameObject数量比较多的情况下,会增加内存的开销和影响游戏的性能。使用对象池可以很好的对这些需要实时刷新的游戏对象进行管理,在需要时从对象池中取(也就是SetActive(true)),不需要时回收到对象池中(也就是SetActive(false))。

2.思路

以下分了三个类来实现对象池的例子。
1.Pool(对象池类)
其中主要有获取对象GetObject()、回收对象ReleaseObject()、清空对象池ClearPool()三个方法。
2.PoolManager(对象池管理类)
用于管理所有的对象池,方法看代码有详细介绍
3.ObjectPoolTest(测试类)
用于测试对象池

代码实现

1.Pool

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

public class Pool
{
    int maxCount; //对象池存储数据的最大数据量
    public string prefabPath; //对象的存储路径
    List<GameObject> usingList = new List<GameObject>(); //正在使用中的对象
    List<GameObject> freeList = new List<GameObject>(0); //对象池中空闲的对象

    public Pool(int count)
    {
        this.maxCount = count;
    }

    /// <summary>
    /// 从对象池中获取对象
    /// </summary>
    /// <returns></returns>
    public GameObject GetObject()
    {
        GameObject obj = null;
        if(freeList.Count > 0) //如果有空闲列表不为空,从从空闲列表中取对象
        {
            obj = freeList[0];
            usingList.Add(obj); //取出的对象放入正在使用对象列表中
            freeList.RemoveAt(0); //从空闲列表中移除取出的对象
        }
        else //当不存在空闲列表时,则根据资源路径去实例化
        {
            if (usingList.Count >= maxCount) //如果当前场景中的对象已经达到对象池的最大上限,直接返回
                return obj;
            GameObject prefab = Resources.Load<GameObject>(prefabPath);
            obj = GameObject.Instantiate(prefab);
            //将实例化的新对象放入正在使用列表中
            usingList.Add(obj);
            int instanceID = obj.GetInstanceID();
            PoolManager.Instance.instanceIDToPathDic.Add(instanceID, prefabPath); //将克隆出来的对象ID和资源路径存如=入对象池管理类中
        }
        return obj;
    }

    /// <summary>
    /// 回收对象
    /// </summary>
    /// <param name="obj">需要回收的对象</param>
    public void ReleaseObject(GameObject obj)
    {
        usingList.Remove(obj);//将需要回收的对象从正在使用列表中移除
        freeList.Add(obj); //讲需要回收的对象加入到空闲列表中
    }
    /// <summary>
    /// 清空对象池
    /// </summary>
    public void ClearPool()
    {
        //销毁使用列表和空闲列表中的对象
        foreach (var item in usingList)
            GameObject.Destroy(item);
        foreach (var item in freeList)
            GameObject.Destroy(item);
        //清空列表
        usingList.Clear();
        freeList.Clear();
    }
}


2.PoolManager

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

public class PoolManager 
{
    static PoolManager instacne; //将对象池管理类设计成单例模式
    public static PoolManager Instance
    {
        get
        {
            if (instacne == null)
                instacne = new PoolManager();
            return instacne;
        }
    }

    public Dictionary<string, Pool> allPoolDic = new Dictionary<string, Pool>(); //存储了所有对象池
    public Dictionary<int, string> instanceIDToPathDic = new Dictionary<int, string>(); //存储了所有对象的实例化ID和该对象对应的对象池的资源路径
    /// <summary>
    /// 从相应的对象池中取得相应的对象
    /// </summary>
    /// <param name="prefabPath"></param>
    /// <returns></returns>
    public GameObject GetObjFormPoll(string prefabPath)
    {
        GameObject obj = null;
        if(allPoolDic.ContainsKey(prefabPath)) //先判断该资源对应的对象池是否存在
        {
            Pool pool = allPoolDic[prefabPath];
            obj = pool.GetObject();
        }
        else
        {
            //当前字典中不存在该对象池,则新创建对象池
            int maxCount = 5;
            Pool pool = new Pool(maxCount);
            pool.prefabPath = prefabPath;
            allPoolDic.Add(prefabPath, pool);
            obj = pool.GetObject();
        }
        //将对象设置成激活状态
        if (obj != null)
            obj.SetActive(true);
        return obj;
    }

    /// <summary>
    /// 将对象还回对象池中
    /// </summary>
    public void ReleaseObjToPool(GameObject obj)
    {
        if (obj == null)
            return;
        int instanceID = obj.GetInstanceID();
        if(instanceIDToPathDic.ContainsKey(instanceID))
        {
            string prefabPath = instanceIDToPathDic[instanceID]; //获取该对象对应的资源路径
            if(allPoolDic.ContainsKey(prefabPath))
            {
                Pool pool = allPoolDic[prefabPath];
                pool.ReleaseObject(obj);
                obj.SetActive(false); //回收后将对象设置为隐藏状态
            }
        }
    }
    /// <summary>
    /// 清空对象池
    /// </summary>
    public void ClearAllPool()
    {
        foreach (var item in allPoolDic.Values)
            item.ClearPool(); //清空每个对象池
        allPoolDic.Clear();
        instanceIDToPathDic.Clear();
    }
}

3.ObjectPoolTest

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

public class ObjectPoolTest : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        //从对象池中创建对象
        if(Input.GetKeyDown(KeyCode.Space))
        {
            Vector3 pos = new Vector3(Random.Range(-5, 5), Random.Range(-5, 5), Random.Range(-5, 5));
            GameObject obj = PoolManager.Instance.GetObjFormPoll("Cube");
            obj.transform.position = pos;
        }
        //回收从对象池创建的对象
        if(Input.GetKeyDown(KeyCode.R))
        {
            GameObject obj = GameObject.Find("Cube(Clone)");
            PoolManager.Instance.ReleaseObjToPool(obj);
        }
    }
}

4.测试结果

在这里插入图片描述
可以看到,无论我们按几次空格键,场景中最多只会生成5个对象,因为限制了对象池最大数量为5,当你不需要使用时,就按R把对象隐藏起来,而不是直接删除掉。下次需要用到对象的时候直接将状态设置为显示,而不是重新实例化。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值