你使用对象池了吗

作者: JervieQin | 来源:发表于2017-09-10 20:58 被阅读149次

什么是对象池?
对象池,简单的说就是一种为了避免重复创建,删除对象的解决方案。它可以通过复用游戏对象,一定程度上提高游戏性能,避免内存消耗,特别针对任何频繁创建删除对象的情景。这符合了性能优化中对脚本优化的理念。是居家旅行...哦不...游戏开发必备技能。
对象池的实现原理?
创建复用对象的集合,通过查找和设置集合元素的active状态,达到和创建、删除一样的视觉效果。



这里先通过一个射击子弹的简单例子,从不用对象池,到给子弹创建对象池,去繁就简的一步步呈现对象池使用过程,深化理解。


不用对象池的小白脚本
Control.cs
...
public class Control:MonoBehaviour{
     public  GameObject bulletPrb;
     void Update(){
          if(Input.GetMouseButton(0))
                  Fire();
  }
     void Fire(){
      Instantiate(bulletPrb);
  }
}

Bullet.cs

...
public class Bullet:Monobehaviour{
     void Start(){
      Invoke("Destroy",2);
  }
     void Destroy(){
      Destroy(gameObject);  
  }
}

这里很容易的脑补出Hierarchry下,bullet对象一直在创建,销毁,创建,销毁。。。

使用对象池的一阶脚本
Control.cs

...
using System.Collections.Generic;

public class Control:MonoBehaviour{
     public  GameObject bulletPrb;

     public int poolingCapacity = 20;
     public List<GameObject> pool;
   
     void Start(){                 //创建对象池,并将对象设为不可见
          pool = new List<GameObject>()
          for(int i=0 ; i<poolingCapacity;i++)
                { 
                    GameObject bullet = Instantiate(bulletPrb);
                    pool.Add(bullet); 
                    pool[i].SetActive(false);
                }         
     }
     void Update(){
          if(Input.GetMouseButton(0))
                  Fire();
     }
     void Fire(){               
         //Instantiate(bulletPrb,transform.position,Quternion.identity);

         for(int i = 0 ; i < pool.Count; i++)
         {        //自动搜索对象池中失活的对象并激活显示
                 if ( !pool[i].activeinHierarchy )
                     {
                          pool[i].transform.position = transform.position;
                          pool[i].transform.rotation = transform.rotation;
                          pool[i].SetActive(true);
                          break;
                     }
         }
  }
}

Bullet.cs

...
public class Bullet:Monobehaviour{
     void OnEnable(){
      Invoke("Destroy",2);
  }
     void Destroy(){
       //Destory(gameObject);
       gameObject.SetActive(false);    //只要隐藏掉就好,对象还在对象池中
  }
     void OnDisable{
        CancelInvoke();
  }
}

这样对象池的功能算是实现了。然而它是被构建在控制脚本中的,这样显然不行。这就像编程小白把所有语句写在主函数中一样,这不符合面向对象的思想!因此,需要第三步

使用对象池的二阶脚本
新建一个ObjectBulletPool.cs 并挂载到新建的空物体ObjectPooler上

 ...
using System.Collections.Generic
public class ObjectPool: MonoBehaviour{
        public static ObjectPool current;

        public GameObject objectPrb;          //将被放入对象池中的预制体
        public int poolCapacity = 20;
        public List<GameObject> pool;
        public bool willgrow = true;           //池子容量不够时,是否自动扩展池子

        void Awake(){
            current = this;
     }
         void Start(){
             for(int i = 0 ; i<poolCapacity ; i++)
              {
                  GameObject obj = Instantiate(objectPrb);
                  pool.Add(obj);
                  obj.SetActive(false);
              }
     }
         public GameObject GetPooledObject(){
            for( int i = 0 ; i<pool.Count ; i++)    //遍历对象池,将未激活的对象传递出去
            {
                if ( ! pool[i].activeInHierarchy )
                        return pool[i];
            }
            if  ( willgrow )  //当池子中所有对象都激活了,但是还想激活显示对象时,扩展池子
            {
                GameObject obj = Instantiate(objectPrb);
                pool.Add(obj);
                obj.SetActive(false);
                return obj;
            }
           return null;
     } 
}

简化 Control.cs

...
using System.Collections.Generic;

public class Control:MonoBehaviour{
/*
     public  GameObject bulletPrb;
     public int poolingCapacity = 20;
     public List<GameObject> pool;
   
     void Start(){                 //创建对象池,并将对象设为不可见
          pool = new List<GameObject>()
           for(int i=0 ; i<poolingCapacity;i++)
               { 
                    GameObject bullet = Instantiate(bulletPrb);
                   pool.Add(bullet); 
                 pool[i].SetActive(false);
             }         
     }
*/
     void Update(){
          if(Input.GetMouseButton(0))
                  Fire();
     }
     void Fire(){               
         //Instantiate(bulletPrb,transform.position,Quternion.identity);
        /*
         for(int i = 0 ; i < pool.Count; i++)
         {        //自动搜索对象池中失活的对象并激活显示
                 if ( !pool[i].activeinHierarchy )
                     {
                          pool[i].transform.position = transform.position;
                          pool[i].transform.rotation = transform.rotation;
                          pool[i].SetActive(true);
                          break;
                     }
          }
        */
            //获取未激活对象,激活显示
          GameObject obj = ObjectPool.current.GetPooledObject();  
          if (obj == null) return;
          obj.transform.position = transform.position;
          obj.transform.rotation = transform.rotation;
          obj.SetActive(true);
  }
}

不用改 Bullet.cs

...
public class Bullet:Monobehaviour{
     void OnEnable(){
      Invoke("Destroy",2);
  }
     void Release(){
       //Destory(gameObject);
       gameObject.SetActive(false);    //只要隐藏掉就好,对象“回到”对象池中
  }
     void OnDisable{
        CancelInvoke();
  }
}

多说一句,如果你能发射不同类型的子弹怎么办?
解决思路:将子弹类型bulletPrb对象改成数组,在GetPooledObject()函数中传入子弹类型的参数,然后遍历数组匹配子弹类型,return出去。

小结:
这个对象池其实是固定的,一旦创建就不用删除,这样便于复用创建好的对象。使用对象其实就是在对象池中搜索没有active的对象并激活获取到它好进行操作。释放对象就是让他失活,好被以后搜索复用。
Ok,看到这里,对象池的基本用法就掌握了,在实际项目中需求会更加多变,但是万变不离本质。高阶运用也是从低阶进化的!



高阶运用请看这位老兄的文章:unity中的通用对象池

相关文章

  • 你使用对象池了吗

    什么是对象池?对象池,简单的说就是一种为了避免重复创建,删除对象的解决方案。它可以通过复用游戏对象,一定程度上提高...

  • Laya_2D示例项目使用对象池预制体的创建、位置设置和添加到父

    //使用对象池创建盒子 1.对象池的使用 对象池创建箱子 let box = Laya.Pool.getI...

  • ARC自动引用计数和分类

    对象自动释放池的使用释放池的作用autorelease对象方法@autoreleasepool关键字的使用Pers...

  • TS:对象池

    对象池项目源码: 使用:

  • 微信小游戏性能优化点

    1. 对象池 多数游戏,频繁创建和销毁同类对象很常见,使用对象池已是基本操作,必须牢记并熟练使用。推荐一个应用示例...

  • creator使用对象池

    使用对象池 在运行时进行节点的创建(cc.instantiate)和销毁(node.destroy)操作是非常耗费...

  • Netty对象池

    在平时工作中,听说和使用过连接池,线程池等.还有一种就是对象池,可以实现对象复用的功能. 当然实现对象池的方式手段...

  • InheritableThreadLocal还存在的问题

    [toc] 问题场景 我们使用线程时候往往不会只是简单的new Thread对象,而是使用线程池,当然使用线程池的...

  • QF框架 对象池

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

  • React细节知识之对象池

    React使用对象池()来管理合成事件对象的创建和销毁,在启动时React分配一个对象池,从对象池中getPo...

网友评论

    本文标题:你使用对象池了吗

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