【Unity编程】Unity3D-使用对象池高效管理内存

Unity编程标准导引-3.4 Unity中的对象池

  本节通过一个简单的射击子弹的示例来介绍Transform的用法。子弹射击本身很容易制作,只要制作一个子弹Prefab,再做一个发生器,使用发生器控制按频率产生子弹,即克隆子弹Prefab,然后为每个子弹写上运动逻辑就可以了。这本该是很简单的事情。不过问题来了,发射出去后的子弹如何处理?直接Destroy吗?这太浪费了,要知道Unity的Mono内存是不断增长的。就是说出了Unity内部的那些网格、贴图等等资源内存(简单说就是继承自UnityEngine下的Object的那些类),而我们自己写的C#代码继承自System下的Object,这些代码产生的内存即是Mono内存,它只增不减。同样,你不断Destroy你的Unity对象也是要消耗性能去进行回收,而子弹这种消耗品实在产生的太快了,我们必需加以控制。
  那么,我们如何控制使得不至于不断产生新的内存呢?答案就是自己写内存池。自己回收利用之前创建过的对象。所以这个章节的内容,我们将重点放在写一个比较好的内存池上。就我自己来讲,在写一份较为系统的功能代码之前,我考虑的首先不是这个框架是该如何的,而是从使用者的角度去考虑,这个代码如何写使用起来才会比较方便,同样也要考虑容易扩展、通用性强、比较安全、减少耦合等等。

本文最后结果显示如下:


3.4.1、从使用者视角给出需求

  首先,我所希望的这个内存池的代码最后使用应该是这样的。

  • Bullet a = Pool.Take<Bullet>(); //从池中立刻获取一个单元,如果单元不存在,则它需要为我立刻创建出来。返回一个Bullet脚本以便于后续控制。注意这里使用泛型,也就是说它应该可以兼容任意的脚本类型。
  • Pool.restore(a);//当使用完成Bullet之后,我可以使用此方法回收这个对象。注意这里实际上我已经把Bullet这个组件的回收等同于某个GameObject(这里是子弹的GameObject)的回收。
      使用上就差不多是这样了,希望可以有极其简单的方法来进行获取和回收操作。

3.4.2、内存池单元结构

  最简单的内存池形式,差不多就是两个List,一个处于工作状态,一个处于闲置状态。工作完毕的对象被移动到闲置状态列表,以便于后续的再次获取和利用,形成一个循环。我们这里也会设计一个结构来管理这两个List,用于处理同一类的对象。
  接下来是考虑内存池单元的形式,我们考虑到内存池单元要尽可能容易扩展,就是可以兼容任意数据类型,也就是说,假设我们的内存池单元定为Pool_Unit,那么它不能影响后续继承它的类型,那我们最好使用接口,一旦使用类,那么就已经无法兼容Unity组件,因为我们自定义的Unity组件全部继承自MonoBehavior。接下来考虑这个内存单元该具有的功能,差不多有两个基本功能要有:

  • restore();//自己主动回收,为了方便后续调用,回收操作最好自己就有。
  • getState();//获取状态,这里是指获取当前是处于工作状态还是闲置状态,也是一个标记,用于后续快速判断。因为接口中无法存储单元,这里使用变通的方法,就是留给实现去处理,接口中要求具体实现需要提供一个状态标记。
      综合内存池单元和状态标记,给出如下代码:
    复制代码
    namespace AndrewBox.Pool
    {
      public interface Pool_Unit
      {
          Pool_UnitState state();
          void setParentList(object parentList);
          void restore();
      }
      public enum Pool_Type
      {
          Idle,
          Work
      }
      public class Pool_UnitState
      {
          public Pool_Type InPool
          {
              get;
              set;
          }
      }
    }
    复制代码

     

    3.4.3、单元组结构

      接下来考虑单元组,也就是前面所说的针对某一类的单元进行管理的结构。它内部有两个列表,一个工作,一个闲置,单元在工作和闲置之间转换循环。它应该具有以下功能:
  • 创建新单元;使用抽象方法,不限制具体创建方法。对于Unity而言,可能需要从Prefab克隆,那么最好有方法可以从指定的Prefab模板复制创建。
  • 获取单元;从闲置表中查找,找不到则创建。
  • 回收单元;将其子单元进行回收。
      综合单元组结构的功能,给出如下代码:
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AndrewBox.Pool
{
    public abstract class Pool_UnitList<T> where T:class,Pool_Unit
    {
        protected object m_template;
        protected List<T> m_idleList;
        protected List<T> m_workList;
        protected int m_createdNum = 0;
        public Pool_UnitList()
        {
            m_idleList = new List<T>();
            m_workList = new List<T>();
        }



        /// <summary>
        /// 获取一个闲置的单元,如果不存在则创建一个新的
        /// </summary>
        /// <returns>闲置单元</returns>
        public virtual T takeUnit<UT>() where UT:T
        {
            T unit;
            if (m_idleList.Count > 0)
            {
                unit = m_idleList[0];
                m_idleList.RemoveAt(0);
            }
            else
            {
                unit = createNewUnit<UT>();
                unit.setParentList(this);
                m_createdNum++;
            }
            m_workList.Add(unit);
            unit.state().InPool = Pool_Type.Work;
            OnUnitChangePool(unit);
            return unit;
        }
        /// <summary>
        /// 归还某个单元
        /// </summary>
        /// <param name="unit">单元</param>
        public virtual void restoreUnit(T unit)
        {
            if (unit!=null && unit.state().InPool == Pool_Type.Work)
            {
                m_workList.Remove(unit);
                m_idleList.Add(unit);
             
  • 13
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值