最近在看教程,学到了一点关于unity对象池的技术,现在来总结一下。

1.适用范围

在游戏中,总会有一些对象会重复出现在游戏场景中,比如敌人、子弹、特效……这些对象如果用一般的生成(Instantiate)及销毁(Destroy)方法来使其出现或消失在游戏场景中时,往往会造成很大的性能开销,尤其是在移动设备,如果大量使用此方法来处理对象的话会使得游戏变卡顿,影响游戏体验。因此可以用对象池技术来解决这个问题。

2.关于对象池

对象池就是将要重复出现在游戏场景中的物体(即可复用对象)在进入游戏场景中时预先生成,将其设置为未激活状态(设置SetActive属性为false,防止其在场景中出现)并存放在对象池中,当要让该物体出现在场景中时,将该物体从对象池中取出,激活并移动至指定地方。而当要让该物体消失于场景中时,则将其放入对象池中,并将其各项数值重置为默认(一些粒子对象是在播放完特效才消失的,便需要将其重置到播放前的状态),再设置其为未激活状态,需要注意的是,每个对象池里存储的可复用物体不能太多。

3.实现思路

以下为我参照教程中的方法整理的对象池实现思路图,可能有些简陋:

Unity自带的对象池性能如何 unity对象池优缺点_unity


以及实现对象池时所用的脚本的功能图:

Unity自带的对象池性能如何 unity对象池优缺点_游戏_02

4.代码

废话不多说,下面贴出所用脚本的代码:

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

//作为静态方法
public class GameObjectUtil  {

    private static Dictionary<RecycleGameObject,ObjectPool> pools=new Dictionary<RecycleGameObject, ObjectPool>();

    //提供一个实例化方法
    public static GameObject Instantiate(GameObject prefab, Vector3 pos)
    {
        GameObject instance;
        RecycleGameObject reference = prefab.GetComponent<RecycleGameObject>();
        //检查是否为可复用对象
        if (reference!=null)
        {
            //取得该预制体的对象池
             var pool= GetObjectPool(reference);
            //在对象池中生成克隆体,得到该克隆体
            instance = pool.NextObject(pos).gameObject;
            return instance;
        }
        else
        {
            //调用monobehaviour自带的实例化方法直接生成克隆体
            instance = GameObject.Instantiate(prefab);
            instance.transform.position = pos;

            return instance;
        }

    }


    /// <summary>
    /// 提供一个销毁方法
    /// </summary>
    /// <param name="prefab">待克隆的预制体</param>
    public static void Destroy(GameObject prefab)
    {
        RecycleGameObject temp = prefab.GetComponent<RecycleGameObject>();
        //检查是否为可复用对象
        if (temp!=null)
        {
            temp.ShutDown();
        }
        else
        {
            Destroy(prefab);
        }

    }

    /// <summary>
    /// 获取对象的对象池
    /// </summary>
    /// <param name="reference"></param>
    /// <returns></returns>
    private static ObjectPool GetObjectPool(RecycleGameObject reference)
    {
        ObjectPool pool;
        //查询字典里有没有该对象相对应的对象池
        if (pools.ContainsKey(reference))
        {
            //得到相应的对象池
            pool = pools[reference];
            return pool;
        }
        else
        {
            //没有对象池的话新建一个对象池
            var poolContainer=new GameObject(reference.gameObject.name+"pool");
            //添加对象池管理组件
            pool = poolContainer.AddComponent<ObjectPool>();
            //将预制体绑定到对象池中
            pool.prefab = reference;
            //将预制体与对象池添加到字典中
            pools.Add(reference,pool);
            return pool;
        }
    }

}

注:其中GameObjectUtil类为静态类,不需要继承monobehaviour类,也无需挂载到游戏对象中。

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

public class ObjectPool : MonoBehaviour {

    //对象池的管理脚本
    public  List<RecycleGameObject> poolInstance=new List<RecycleGameObject>();

    public RecycleGameObject prefab;


    /// <summary>
    /// 创建预制体的克隆体
    /// </summary>
    /// <param name="pos">克隆体的位置</param>
    /// <returns></returns>
    private RecycleGameObject CreateObject(Vector3 pos)
    {
        //实例化克隆体
        var clone=GameObject.Instantiate(prefab);
        //设置克隆体的位置
        clone.transform.position = pos;
        //将克隆体作为对象池的子对象
        clone.transform.parent = transform;
        //将克隆体加入到列表中
        poolInstance.Add(clone);
        return clone;

    }


    /// <summary>
    /// 取出克隆体
    /// </summary>
    /// <param name="pos">克隆体的位置</param>
    /// <returns></returns>
    public RecycleGameObject NextObject(Vector3 pos)
    {
        RecycleGameObject instance = null;
        //调用方法获取克隆体实例
        instance = CreateObject(pos);
        //激活克隆体
        instance.Restart();

        return instance;
    }

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

public class RecycleGameObject : MonoBehaviour {

    //通过设置游戏对象的SetActive属性来激活/关闭游戏对象,并作为判断此物体是否为可复用对象的标准
    public void Restart()
    {
        gameObject.SetActive(true);
    }

    public void ShutDown()
    {
        gameObject.SetActive(false);
    }
}

5.使用方法

只需要在预制件里挂上RecycleGameObject脚本,就能将此物体作为可复用的物体。调用GameObjectUtil类里面的Instantiate方法就能实例化物体及其对象池。比如我要生成个对象池,里面有3个物体。那我可以这样写个脚本来调用。

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

public class Test : MonoBehaviour
{

    public int mun;
    public GameObject prefab;
    private Vector3 pos;

    // Use this for initialization
    void Start ()
    {
        pos = transform.position+Vector3.right;

    }

    // Update is called once per frame
    void Update () {
        if (mun>0)
        {

            GameObjectUtil.Instantiate(prefab,pos);
            mun--;
        }
    }
}

在场景中放入一个Cube,挂载上此脚本,如图设置:

Unity自带的对象池性能如何 unity对象池优缺点_Unity自带的对象池性能如何_03


为脚本中的prefab指定物体,在这里我指定了一个Sphere,Sphere里挂载了RecycleGameObject脚本,运行情况如图:

Unity自带的对象池性能如何 unity对象池优缺点_游戏_04

Unity自带的对象池性能如何 unity对象池优缺点_游戏_05


可以看到对象池里有3个对象,且对象池中的物体都挂载了RecycleGameObject脚本。