美文网首页
Eevee框架4——对象池

Eevee框架4——对象池

作者: ShawnWeasley | 来源:发表于2020-08-18 16:02 被阅读0次

这里先简述一下对象池的目的以及什么情况下需要对象池:

将部分需要持续性复用的物体(或对象)以隐藏/修改位置等方式先收集起来,等需要的时候直接打开而不需要重新实例化出来。
牺牲部分内存,降低CG垃圾回收频率(因为CG比较影响帧数)。
举个例子比如Fps游戏中的子弹,需要高频率生成,这时候子弹用完了直接藏起来,再用的时候打开就是了,弹痕也一样。
再比如一些Moba游戏中的小兵,其实都是重复的,在他们“死亡”时关闭就行了,生成时再重置一下参数打开即可。

什么情况下不需要使用对象池呢:

某些物体并非高频使用的,比如第一关的大Boss,他并不会在后面出现,因此不需要把他放到对象池中。

本人常做的一些展示类项目中,其实并不太需要使用到对象池,因为展示类模型一般会比较大,也很少出现来回切换的情况,这时候在加载过程中牺牲一些性能释放掉反而比你一直将模型放在内存中的好,但这里我们还是来创建一个比较好的对象池。

市面上的对象池大多用Dictionary来处理,目的是把不同的物体放到一个池子里,然后通过名字来检索。
个人认为这并不是对象池正确的用法,应该是同样的物体,放到单独的一个池子里。如果物体类型不同,没必要放到一个大的储存池里。因此每一个类型的物体会有一个单独的Pool。



调用方式:

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

public class Test : MonoBehaviour
{
    //一个用于管理生成物体的List
    public List<GameObject> m_CubeList = new List<GameObject>();
    //需要实例化出来的物体
    public GameObject m_Cube;
    //对象池
    ObjectPool cubePool;
    //临时变量
    GameObject tempCube;

    private void Start()
    {
        //创建一个对象池,方法一
        cubePool = new ObjectPool();
        cubePool.ElementObject = m_Cube;
        //因为写了三种构造函数,也可以这么创建,方法二
        cubePool = new ObjectPool(m_Cube);
        //或者这样,自定义对象池场景中GameObject父物体的名字,方法三
        cubePool = new ObjectPool("子弹暂存列表");
        cubePool.ElementObject = m_Cube;
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //按下鼠标左键获取一个对象,但该对象的实例化方法还是本类实现的
            tempCube = cubePool.GetObject(InstantiateCube);
            //操作生成的这个对象
            tempCube.transform.parent = this.transform;
            //存起来,用于还回去的时候调用
            m_CubeList.Add(tempCube);
        }
        if (Input.GetMouseButtonDown(1))
        {
            if (m_CubeList.Count > 0)
            {
                //此处仅写了一个简单的还回去的示例,可根据需要自行执行销毁
                cubePool.PushObject(m_CubeList[0]);
                m_CubeList.RemoveAt(0);
            }
        }
    }

    /// <summary>
    /// 此方法为GetObject的参数,可在这里自行实现加载及序列化方法
    /// </summary>
    /// <param name="gameObject"></param>
    /// <returns></returns>
    GameObject InstantiateCube(GameObject gameObject)
    {
        return Instantiate(gameObject);
    }
}

对象池代码:

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

/// <summary>
/// 对象池(重复调用/使用的游戏物体) 
/// </summary>
public class ObjectPool
{
    //用于储存的隐藏父物体
    private GameObject m_RootObject;
    //用于实例化的物体
    private GameObject m_ElementObject;
    //用于储存的对象池
    private Stack<GameObject> m_ObjectPoolStack = new Stack<GameObject>();

    /// <summary>
    /// 构造函数,要求必须设置一个ElementObject物体,即用于复用的物体
    /// </summary>
    /// <param name="elementObject"></param>
    public ObjectPool(GameObject elementObject)
    {
        //此处使用了StringEx
        //如不使用StringEx可用下面一行代替
        //m_RootObject = new GameObject(elementObject.name + "Stack");
        m_RootObject = new GameObject(StringEx.Concat(elementObject.name, "Stack"));
        m_RootObject.gameObject.SetActive(false);
        m_ElementObject = elementObject;
    }

    /// <summary>
    /// 设置收集父物体的名字
    /// </summary>
    /// <param name="name"></param>
    public ObjectPool(string name)
    {
        m_RootObject = new GameObject(name);
        m_RootObject.gameObject.SetActive(false);
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    public ObjectPool()
    {
        m_RootObject = new GameObject("UnnamedObjectPool");
        m_RootObject.gameObject.SetActive(false);
    }

    /// <summary>
    /// 需要生成的物体
    /// </summary>
    public GameObject ElementObject
    {
        get { return m_ElementObject; }
        set { m_ElementObject = value; }
    }

    /// <summary>
    /// 获得一个物体
    /// </summary>
    /// <param name="instantiateAction">需实现一个返回值为GameObject的实例化函数</param>
    /// <returns></returns>
    public GameObject GetObject(Func<GameObject, GameObject> instantiateAction)
    {
        if (m_ObjectPoolStack.Count == 0)
        {
            return instantiateAction(m_ElementObject);
        }
        else
        {
            return m_ObjectPoolStack.Pop();
        }
    }

    /// <summary>
    /// 归还一个物体
    /// </summary>
    /// <param name="pushObject"></param>
    public void PushObject(GameObject pushObject)
    {
        m_ObjectPoolStack.Push(pushObject);
        pushObject.transform.parent = m_RootObject.transform;
    }

    /// <summary>
    /// 清除当前列表
    /// </summary>
    public void Clear()
    {
        m_ObjectPoolStack.Clear();
    }
}

虽然注释写的很清楚了,但还是担心小白会看不懂,这里我的主要思路是每个池子只管自己类型的对象(比如子弹),new一个Pool就是一个子弹的Pool,如果弹痕也需要,那就再new一个弹痕的Pool,有关这些Pool本身的管理我觉得可以根据用户自己的需要去实现就行。
因此我这里把实例化的代码放在使用类当中,略微提升一下通用性。这个对象池其实只是实现了一个Stack的暂存和取用。当然了,本人对对象池的需求不多,因此仅理解到这里应该能满足自身需要了,其他小伙伴如果更倾向于Dictionary来处理,序中的链接中有相对应的案例,直接百度也能搜到很多。这里就不赘述了。

相关文章

  • Eevee框架4——对象池

    这里先简述一下对象池的目的以及什么情况下需要对象池: 将部分需要持续性复用的物体(或对象)以隐藏/修改位置等方式先...

  • 游戏框架与组件(1):对象池

    本文介绍了对象池以及对象池的代码实现。 本文链接游戏框架与组件(1):对象池[https://www.jiansh...

  • Eevee框架10——UI框架

    想要一个完美的UI框架,貌似太理想了,这里参考了唐老师、siki等能百度到的很多UI框架,发现大家都只是做了界面的...

  • QF框架 对象池

    使用案例 注 需要使用对象池的对象都要继承这两个接口 IPoolable, IPoolType

  • Eevee框架1——序

    一个多月来学了很多东西,想综合起来做一个自己的框架,那就从今天开始,构建一个自己用着趁手的框架吧。 其实凉鞋的QF...

  • 面向对象编程套路

    1、对象池2、数据缓存对象3、管道4、事件队列与消息循环熟悉对象之间的协作与三种消息交换方式在开发框架中寻找上述套...

  • Eevee框架2——单例

    这里我们跟唐老师一样,先创建一个单例的基类,这样后续很多类都可以简单地实现单例。如果不了解单例的,唐老师的免费前几...

  • Eevee框架8——场景切换

    和对象池一样,本人场景切换用到的非常少(基本单场景搞定,因为个人感觉Unity自己加载第一个场景的速度,要远快于动...

  • Eevee框架7——Mono工具

    目前为止,我们已经有了单例、基于Mono的单例、对象池、事件系统四个轮子(工具),还需要继续造一些常用的工具。 一...

  • Eevee框架11——Json处理

    本篇开始我们开始进行数据的处理,在数据处理、资源处理完成后,我们就进行框架整合。数据处理首先是我们常用的Json处...

网友评论

      本文标题:Eevee框架4——对象池

      本文链接:https://www.haomeiwen.com/subject/gqjhjktx.html