Unity零基础到进阶 ☀️| Unity中的对象池技术 ObjectPool 定义 + 实例演示

15,660 阅读4分钟

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

对象池

1.对象池简单定义

对象池是一种Unity经常用到的内存管理服务,针对需要经常生成消失的对象,作用在于可以减少创建每个对象的系统开销。我们在对象需要消失的时候不Destroy而是SetActive(false),然后放入池子中(Queue),当需要再次显示一个新的对象的时候,先去池子中看有没有隐藏的对象,有就取出SetActive(true),若池子里没有可用的则再Instantiate。

2.使用对象池的原因

在Unity游戏开发的过程中经常会创建一些新的对象,如果数量较少还可以接受,如果创建的新对象数量庞大,那么对内存而言是一个极大的隐患。例如射击游戏当中,每发射一颗子弹,都要创建一个新的子弹对象,那么子弹是数量庞大,可想而知一场游戏当中会创建多少这样的新对象,那么如果这些子弹创建之后都对游戏起着关键且持续性的作用也无可厚非,问题是子弹发射完成之后,几秒之后就不再拥有任何的意义,一般会将它自动的隐藏,也就是我们所说的SetActive(false),因此大量的非活跃对象出现在游戏场景当中。

由于对象池中的对象只是SetActive(false)了,并非真正销毁了,所以一般运用于游戏中经常用到或需大量生成的物体。并不能将所有对象都用此技术,如若不然便是得不偿失了。

3.创建并使用一个简单对象池

首先我们先生成一个对象池的class,里面的内容就很简单,首先有一个Queue用来存放池子中的对象,然后实现两个方法,一个取对象,一个放对象。取对象的时候,若池子中有可用对象则取出一个,若没有则Instantiate一个;放对象即将对象SetActive(false)并且放入池子中。 下图是一个简单的效果 在这里插入图片描述

首先有一个Singleton.cs 用于将其他脚本写成单例模式

namespace Utilty
{
    public class Singleton<T>
    {
        private static T instance;
        public static T GetInstance()
        {
            if (instance == null)
            {
                instance = (T)Activator.CreateInstance(typeof(T), true);
            }
            return instance;
        }
    }
}

AssetsManager.cs 用于加载生成游戏对象

using System.Collections.Generic;
using UnityEngine;

//通用框架
namespace Utilty
{
    public class AssetsManager : Singleton<AssetsManager>
    {
        protected AssetsManager()
        {
            assetsCache = new Dictionary<string, Object>();
        }
        //缓存字典
        private Dictionary<string, Object> assetsCache;

        //获取资源
        public virtual T GetAssets<T>(string path) where T : Object
        {
            //先查看缓存池中有没有这个资源
            if (assetsCache.ContainsKey(path))
            {
                //直接将这个资源返回
                return assetsCache[path] as T;
            }
            else
            {
                //通过Resource.Load去加载资源
                T assets = Resources.Load<T>(path);
                //将新资源加载到缓存池里
                assetsCache.Add(path,assets);
                //返回资源
                return assets;
            }
        }
        //卸载未使用的资源
        public void UnloadUnusedAssets()
        {
            //卸载
            Resources.UnloadUnusedAssets();
        }
    }
    //加载图片
    public class SpriteManager : Singleton<SpriteManager>
    {
    }
    public class PrefabManager : Singleton<PrefabManager>
    {
        private PrefabManager() { }
        public GameObject CreateGameObjectByPrefab(string path)
        {
            //获取预设体
            GameObject prefab = AssetsManager.GetInstance().GetAssets<GameObject>(path);
            //生成
            GameObject obj = Object.Instantiate(prefab);
            // 返回
           return obj;
        }
        public GameObject CreateGameObjectByPrefab(string path, Vector3 pos, Quaternion qua)
        {
            //生成对象
            GameObject obj = CreateGameObjectByPrefab(path);
            //设置坐标和旋转
            obj.transform.position = pos;
            obj.transform.rotation = qua;
            //返回
            return obj;
        }

        public GameObject CreateGameObjectByPrefab(string path, Transform parent, Vector3 localPos, Quaternion localQua)
        {
            //生成对象
            GameObject obj = CreateGameObjectByPrefab(path);
            //设置父物体
            obj.transform.SetParent(parent);
            //设置坐标和旋转
            obj.transform.localPosition = localPos;
            obj.transform.localRotation = localQua;
            //返回
            return obj;
        }
    }
}

ObjectPool.cs 用于通过对象池 生成和回收 游戏对象

using System.Collections.Generic;
using UnityEngine;

namespace Utilty
{
   public class ObjectPool : Singleton<ObjectPool>
{
    //私有构造
    private ObjectPool()
    {
        pool = new Dictionary<string, List<GameObject>>();
    }

    //对象池
    private Dictionary<string, List<GameObject>> pool;
    //通过对象池生成游戏对象
    public GameObject SpawnObject(string name)
    {
        GameObject needObj;
        //查看是否有该名字所对应的子池,且子池中有对象
        if (pool.ContainsKey(name) && pool[name].Count > 0)
        {
            //将0号对象返回
            needObj = pool[name][0];
            //将0号对象从List中移除
            pool[name].RemoveAt(0);
        }
        else
        {
            //直接通过Instantiate生成
            needObj = PrefabManager.GetInstance().CreateGameObjectByPrefab(name);
            //修改名称(去掉Clone)
            needObj.name = name;
        }
        //设置为激活
        needObj.SetActive(true);
        //返回
        return needObj;
    }

        /// <summary>
        /// 回收游戏对象到对象池
        /// </summary>
        /// <param name="Objname"></param>
        public void RecycleObj(GameObject Objname)
    {
            //防止被看到,设置为非激活哦
            Objname.SetActive(false);
            if (pool.ContainsKey(Objname.name))
            {
                //将当前对象放入对象子池
                pool[Objname.name].Add(Objname);
            }
            else
            {
                //创建该子池并将对象放入
                pool.Add(Objname.name, new List<GameObject> { Objname });
         }
    }
}
}

PoolObject.cs 用于延迟回收对象

using System.Collections;
using UnityEngine;
using Utilty;

public class PoolObject : MonoBehaviour
{
    private void OnEnable()
    {
        StartCoroutine(DelayRececle(2));
    }
    //延迟回收协程
    IEnumerator DelayRececle(float interval)
    {
        //等待几秒
        yield return new WaitForSeconds(interval);
        //回收当前对象
        ObjectPool.GetInstance().RecycleObj(gameObject);
    }
}

Demo.cs 用于简单的使用对象池技术

using UnityEngine;
using Utilty;

public class Demo : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.J))
        {
            GameObject go = ObjectPool.GetInstance().SpawnObject("Prefab");
            go.transform.position = Vector3.zero;
            go.transform.rotation = Quaternion.identity;
            go.GetComponent<Rigidbody>().velocity = Vector3.zero;
        }
    }
}