(二)性能优化之对象池

游戏可谓是吃CPU和GPU的大户,虽然我等做的是个极小的游戏,但凡事从小做起,那怕只有那么一点优化的空间,也要把它榨出来,才是我们的追求。
进入正题:

性能优化之对象池

我们知道游戏里有很多东西是需要动态创建的,像,子弹,敌人,塔等物体。如果东西用完了怎么办,销毁呗,Destroy(Object)多简单。但我们要知道实例化(Instantiate)物体的执行过程是比较慢的。同时大量的Instantiate和Destroy对应到底层就是大量的申请,释放内存,结果就是造成了大量的内存碎片。想想在这个塔防游戏里满天飞的子弹导弹,一秒要创建几十次,一秒后又得把他删除,CPU不难受就怪了
创建的导弹
既然不要把物体Destory,那我们如何通过另一种方式“销毁”呢,其实我们销毁的本质不过是让玩家看不到嘛,那方式就多了去了,常用的还是gameObject.SetActive(false),
还有其实最快的是直接移出相机外,这也是一种“销毁”嘛, 我用的还是设置状态,因为设置的时候Unity有回调函数的执行有时候比较方便

    /// <summary>
    /// 激活时调用
    /// </summary>
    private void OnEnable()
    {
        
    }
    /// <summary>
    /// 关闭时调用
    /// </summary>
    private void OnDisable()
    {
        
    }

OnEnable();和OnDisable();会在每次激活或关闭后都执行
OK,这一大堆动态创建的物体总算不用从内存中消失了,那我们当然要对这群东西进行管理了, 对象池就这么来了,顾名思义,把对象都放到一个池子里,需要时把它初始化一下就拿出来用。
首先我们得有个总的对象池
里面有各种具体的池,如:某种子弹的池子,某个怪的池子。
我们将这些池子以字典形式存储

using System.Collections.Generic;
using UnityEngine;

//总的对象池,里面对不同的池进行管理
public class ObjectPool : Singleton<ObjectPool>
{
    //资源所在目录
    public string ResourceDir = "";

	//字典形式存储
    Dictionary<string, SubPool> m_pools = new Dictionary<string, SubPool>();

    //取对象
    public GameObject Spawn(string name)
    {
        if (!m_pools.ContainsKey(name))
            RegisterNew(name);
        SubPool pool = m_pools[name];
        return pool.Spawn();
    }

    //回收对象
    public void Unspawn(GameObject go)
    {
        SubPool pool = null;

        foreach (SubPool p in m_pools.Values)
        {
            if (p.Contains(go))
            {
                pool = p;
                break;
            }
        }
        pool.Unspawn(go);
    }

    //回收所有对象
    public void UnspawnAll()
    {
        foreach (SubPool p in m_pools.Values)
            p.UnspawnAll();
    }

    //创建新子池子
    void RegisterNew(string name)
    {
        //预设路径
        string path = "";
        if (string.IsNullOrEmpty(ResourceDir.Trim()))
            path = name;
        else
            path = ResourceDir + "/" + name;

        //加载预设
        GameObject prefab = Resources.Load<GameObject>(path);

        //创建子对象池
        SubPool pool = new SubPool(transform, prefab);
        m_pools.Add(pool.Name, pool);
    }
}

接下来就是具体的池子了,里面存的就是我们动态创建的某种物体

using System.Collections.Generic;
using UnityEngine;

public class SubPool
{
    Transform m_parent;

    //预设
    GameObject m_prefab;
    
    //集合
    List<GameObject> m_objects = new List<GameObject>();

    //名字标识
    public string Name
    {
        get { return m_prefab.name; }
    }

    //构造
    public SubPool(Transform parent, GameObject prefab)
    {
        this.m_parent = parent;
        this.m_prefab = prefab;
    }

    //取对象
    public GameObject Spawn()
    {
        GameObject go = null;

        foreach (GameObject obj in m_objects)
        {
            if (obj==null)
            {
                m_objects.Remove(obj);
            }
            else if (!obj.activeSelf)
            {
                go = obj;
                break;
            }
        }

        if (go == null)
        {
            go = UnityEngine.Object.Instantiate(m_prefab, m_parent, true);
            m_objects.Add(go);
        }

        go.SetActive(true);
        go.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver);
        return go;
    }

    //回收对象
    public void Unspawn(GameObject go)
    {
        if(Contains(go))
        {
            go.SendMessage("OnUnspawn", SendMessageOptions.DontRequireReceiver);
            go.SetActive(false);
        }
    }

    //回收该池子的所有对象
    public void UnspawnAll()
    {
        foreach(GameObject item in m_objects)
        {
            if (item==null)
            {
                m_objects.Remove(item);
            }
            else if (item.activeSelf)
            {
                Unspawn(item);
            }
        }
    }

    //是否包含对象
    public bool Contains(GameObject go)
    {
        return m_objects.Contains(go);
    }
}

对象池就这么写完了,当然为了有一个更好的规范,我们还可加上一个接口,让物体用接口后受对象池的管理

public interface IReusable
{
    //当取出时调用
    void OnSpawn();

    //当回收时调用
    void OnUnspawn();
}

来写个流程图更好的展示对象池是怎么工作的

Created with Raphaël 2.2.0 对象池收到 创建物体A的命令 判断是否已经存在 保存物体A的池子? 池子遍历集合 判断是否有可用的对象 (状态为false就是可用) 把物体状态设置 为true,并返回 外部对该对 象初始化 实例化对象A 创建新池子 yes no yes no

直观感受下
在发射导弹前对象池有很多状态为false的物体
在这里插入图片描述发射导弹的时候,池子里的导弹对象被拿出来用,状态激活
在这里插入图片描述导弹爆炸,回收对象,状态设为false
同时可以看到爆炸的粒子效果也是对象池里的物体,在此时被激活
(橙色圈里的Bomb为爆炸粒子效果)
在这里插入图片描述下一次发射同样利用这些对象
在这里插入图片描述
看到没,对资源的循环利用体现到了极致啊,这样的激活-关闭的模式极大提高了流畅度,同时避免了内存碎片。

原理与机制大概就是这样了,在unity里一定要注意,场景退出的时候,它会把场景里的物体统统销毁,这肯定造成大问题,我们就把这些对象放在game物体下,game是一个空物体,它上面挂载了整个游戏的管理脚本,并且设置为退出场景不销毁。这样他的子物体在退出场景的时候就不会销毁了,而是发送一个退出场景的消息,对象池收到后会自动地关闭已经激活的物体。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值