Unity对象池初探

理论参考:http://www.cnblogs.com/mezero/p/3955130.html

代码参考:http://www.omuying.com/article/78.aspx


背景:首先为什么会去了解对象池技术,原因是我的游戏在iOS上运行时出现了问题,有时会闪退,于是网上寻找解决方法,大神给出的答案是内存爆了。因为游戏中会频繁而且相对大量的创建( Instantiate  )和( Destroy  ),不仅导致游戏整个性能降低,而且如果创建的数量过多,内存很容易爆了。

目的:研究并实现对象池技术。

每次我都会先去看技术的理论知识,网上很多实现对象池技术的代码,但是理论就没有过多的介绍,因为对象池本身就很简洁形象。对象池技术就是:第一次创建的时候就将对象存储在一个池子中,当需要销毁的时候不直接销毁而是隐藏,当需要时在再次使用,就不需要再次实例化一个新的对象,直接把隐藏的对象显示并拿出来用。如果对象池中已经被隐藏的物体太久没有被重新使用,应该被真正的销毁。


池的最重要的特性,也就是对象池设计模式的本质是允许我们获取一个“新的”对象而不管它真的是一个新的对象还是循环使用的对象。


理论还是很容易理解的,估计有的人看了理论就可以自己去实现了~我没有那么大神,所以去网上找了很多代码资源自己看,去理解。接下来我把怎样实现一个对象池并应用于unity中的思路和代码写出来。


首先根据对象池概念,我们要怎样创建一个可以随时利用的对象池、怎样把实例化的物体放进对象池、怎样从对象池中取出对象、怎样实现超时的物体可以自动删除。


为了解决以上问题,首先我们要分好层级:对象池管理层(PoolManager),对象池组层(PoolItem),对象池组中成员层(PoolItemTime)。简单解释一下:

1:对象池管理层(PoolManager)这个很容易理解,即直接进行对象池操作的脚本。

2:对象池组层(PoolItem),为什么要分组呢?因为我们存进对象池同类型的东西可能不止一个,比如我生成一个Cube放进对象池中,如果在上一个Cube还没有被隐藏的时候我又需要一个,那么这个时候我必须从新生成一个Cube,因为对象池中并没有可以重复使用的对象(即被隐藏的Cube)。

3:对象池组中成员层(PoolItemTime),这个类的名字我是直接采用参考代码的名字,这个类就是用来管理单个物体的。


接下来:

1:我们要怎样创建一个可以随时利用的对象池,我们可以直接在对象池管理类(PoolManager)里面把要用的函数直接用Static修饰就可以了(类似于文档操作之类的),这样其他的脚本可以随时调用对象池操作。

2:怎样把实例化的物体放进对象池,我们需要Push()操作来完成,其中有一个关键性的问题就是,不同类的对象很容易管理,用一个Dictionary<T, T>就可以了,同类的怎么放在一起呢?仔细研究后发现自己思维受限,同类的物品直接用一个类似数组的东西存不就好了吗?下面的代码选择用Dictionary<int, GameObject>存起来,int是获取对象的HashCode存入,其实这个并没有什么用....跟数组的效果其实是一样的, 看3就知道为什么没用了。

3:怎样从对象池中取出对象,首先找到对象所在组,然后取组中第一个对象即可,知道为什么HashCode没用了吧?你也可以将2的int全部存成1。


解决上上面的问题其实整个对象池就没什么了,下面贴上代码,里面的注释和我的解释对应,应该很容易明白的。


PoolItemTime.cs

[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class PoolItemTime {  
  5.   
  6.     ///<summary>  
  7.     /// 对象  
  8.     /// </summary>  
  9.     public GameObject gameObject;  
  10.   
  11.     ///<summary>  
  12.     /// 存取时间  
  13.     /// </summary>  
  14.     public float aliveTime;  
  15.   
  16.     ///<summary>  
  17.     /// 销毁状态  
  18.     /// </summary>  
  19.     public bool destoryStatus;  
  20.   
  21.     public PoolItemTime(GameObject _gameObject){  
  22.         this.gameObject = _gameObject;  
  23.         this.destoryStatus = false;  
  24.     }  
  25.   
  26.     ///<summary>  
  27.     /// 激活对象,将对象显示  
  28.     /// </summary>  
  29.     public GameObject Active(){  
  30.         this.gameObject.SetActive (true);  
  31.         this.destoryStatus = false;  
  32.         aliveTime = 0;  
  33.         return this.gameObject;  
  34.   
  35.     }  
  36.   
  37.     ///<summary>  
  38.     /// 销毁对象,不是真正的销毁  
  39.     /// </summary>  
  40.     public void Destroy(){//重置对象状态  
  41.         this.gameObject.SetActive (false);  
  42.         this.destoryStatus = true;  
  43.         this.aliveTime = Time.time;  
  44.     }  
  45.   
  46.     ///<summary>  
  47.     /// 检测是否超时,返回true或false,没有其他的操作  
  48.     /// </summary>  
  49.     public bool IsBeyondAliveTime(){  
  50.         if (!this.destoryStatus)  
  51.             return false;  
  52.         if (Time.time - this.aliveTime >= PoolManager.Alive_Time) {  
  53.             Debug.Log ("已超时!!!!!!");  
  54.             return true;  
  55.         }  
  56.         return false;  
  57.     }  
  58. }  


PoolItem.cs
[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections.Generic;  
  3.   
  4. public class PoolItem{  
  5.   
  6.     /// <summary>  
  7.     /// 名称,作为标识  
  8.     /// </summary>  
  9.     public string name;  
  10.   
  11.     /// <summary>  
  12.     /// 对象列表,存储同一个名称的所有对象  
  13.     /// </summary>  
  14.     public Dictionary<int, PoolItemTime> objectList;  
  15.   
  16.     public PoolItem(string _name)  
  17.     {  
  18.         this.name = _name;  
  19.         this.objectList = new Dictionary<int, PoolItemTime> ();  
  20.     }  
  21.   
  22.     /// <summary>  
  23.     /// 添加对象,往同意对象池里添加对象  
  24.     /// </summary>  
  25.     public void PushObject(GameObject _gameObject)  
  26.     {  
  27.         int hashKey = _gameObject.GetHashCode ();  
  28.         if (!this.objectList.ContainsKey (hashKey)) {  
  29.             this.objectList.Add (hashKey, new PoolItemTime (_gameObject));  
  30.         } else {  
  31.             this.objectList [hashKey].Active ();  
  32.         }  
  33.     }  
  34.   
  35.     /// <summary>  
  36.     /// 销毁对象,调用PoolItemTime中的destroy,即也没有真正销毁  
  37.     /// </summary>  
  38.     public void DestoryObject(GameObject _gameObject){  
  39.         int hashKey = _gameObject.GetHashCode ();  
  40.         if (this.objectList.ContainsKey (hashKey)) {  
  41.             this.objectList [hashKey].Destroy ();  
  42.         }  
  43.     }  
  44.   
  45.     /// <summary>  
  46.     /// 返回没有真正销毁的第一个对象(即池中的destoryStatus为true的对象)  
  47.     /// </summary>  
  48.     public GameObject GetObject(){  
  49.         if (this.objectList == null || this.objectList.Count == 0) {  
  50.             return null;  
  51.         }  
  52.         foreach (PoolItemTime poolIT in this.objectList.Values) {  
  53.             if (poolIT.destoryStatus) {  
  54.                 return poolIT.Active ();  
  55.             }  
  56.         }  
  57.         return null;  
  58.     }  
  59.   
  60.     /// <summary>  
  61.     /// 移除并销毁单个对象,真正的销毁对象!!  
  62.     /// </summary>  
  63.     public void RemoveObject(GameObject _gameObject){  
  64.         int hashKey = _gameObject.GetHashCode ();  
  65.         if (this.objectList.ContainsKey (hashKey)) {  
  66.             GameObject.Destroy (_gameObject);  
  67.             this.objectList.Remove (hashKey);  
  68.         }  
  69.     }  
  70.   
  71.     /// <summary>  
  72.     /// 销毁对象,把所有的同类对象全部删除,真正的销毁对象!!  
  73.     /// </summary>  
  74.     public void Destory(){  
  75.         IList<PoolItemTime> poolIList = new List<PoolItemTime> ();  
  76.         foreach (PoolItemTime poolIT in this.objectList.Values) {  
  77.             poolIList.Add (poolIT);  
  78.         }  
  79.         while (poolIList.Count > 0) {  
  80.             if (poolIList [0] != null && poolIList [0].gameObject != null) {  
  81.                 GameObject.Destroy (poolIList [0].gameObject);  
  82.                 poolIList.RemoveAt (0);  
  83.             }  
  84.         }  
  85.         this.objectList = new Dictionary<int, PoolItemTime> ();  
  86.     }  
  87.   
  88.     /// <summary>  
  89.     /// 超时检测,超时的就直接删除了,真正的删除!!  
  90.     /// </summary>  
  91.     public void BeyondObject(){  
  92.         IList<PoolItemTime> beyondTimeList = new List<PoolItemTime> ();  
  93.         foreach (PoolItemTime poolIT in this.objectList.Values) {  
  94.             if (poolIT.IsBeyondAliveTime ()) {  
  95.                 beyondTimeList.Add (poolIT);  
  96.             }  
  97.         }  
  98.         int beyondTimeCount = beyondTimeList.Count;  
  99.         for (int i = 0; i < beyondTimeCount; i++) {  
  100.             this.RemoveObject (beyondTimeList [i].gameObject);  
  101.         }  
  102.     }  
  103. }  


PoolManager.cs
[csharp]  view plain  copy
  1. using UnityEngine;  
  2. using System.Collections.Generic;  
  3.   
  4. public class PoolManager {  
  5.   
  6.     /// <summary>  
  7.     /// 超时时间  
  8.     /// </summary>  
  9.     public const int Alive_Time = 1 * 60;  
  10.   
  11.     /// <summary>  
  12.     /// 对象池  
  13.     /// </summary>  
  14.     public static Dictionary<string, PoolItem> itemList;  
  15.   
  16.     /// <summary>  
  17.     /// 添加一个对象组  
  18.     /// </summary>  
  19.     public static void PushData(string _name){  
  20.         if (itemList == null)  
  21.             itemList = new Dictionary<string, PoolItem> ();  
  22.         if (!itemList.ContainsKey (_name))  
  23.             itemList.Add (_name, new PoolItem (_name));  
  24.     }  
  25.   
  26.     /// <summary>  
  27.     /// 添加单个对象(首先寻找对象组->添加单个对象)  
  28.     /// </summary>  
  29.     public static void PushObject(string _name, GameObject _gameObject){  
  30.         if (itemList == null || !itemList.ContainsKey (_name))  
  31.             PushData (_name);//添加对象组  
  32.         //添加对象  
  33.         itemList [_name].PushObject (_gameObject);  
  34.     }  
  35.   
  36.     /// <summary>  
  37.     /// 移除单个对象,真正的销毁!!  
  38.     /// </summary>  
  39.     public static void RemoveObject(string _name, GameObject _gameObject){  
  40.         if (itemList == null || !itemList.ContainsKey (_name))  
  41.             return;  
  42.         itemList [_name].RemoveObject (_gameObject);  
  43.     }  
  44.   
  45.     /// <summary>  
  46.     /// 获取缓存中的对象  
  47.     /// </summary>  
  48.     public static GameObject GetObject(string _name){  
  49.         if (itemList == null || !itemList.ContainsKey (_name)) {  
  50.             return null;  
  51.         }  
  52.         return itemList [_name].GetObject ();  
  53.     }  
  54.   
  55.     /// <summary>  
  56.     /// 销毁对象,没有真正的销毁!!  
  57.     /// </summary>  
  58.     public static void DestroyActiveObject(string _name, GameObject _gameObject){  
  59.         if (itemList == null || !itemList.ContainsKey (_name)) {  
  60.             return;  
  61.         }  
  62.         itemList [_name].DestoryObject (_gameObject);  
  63.     }  
  64.   
  65.     /// <summary>  
  66.     /// 处理超时对象  
  67.     /// </summary>  
  68.     public static void BeyondTimeObject(){  
  69.         if (itemList == null) {  
  70.             return;  
  71.         }  
  72.         foreach (PoolItem poolI in itemList.Values) {  
  73.             poolI.BeyondObject ();  
  74.         }  
  75.     }  
  76.   
  77.     /// <summary>  
  78.     /// 销毁对象,真正的销毁!!  
  79.     /// </summary>  
  80.     public static void Destroy(){  
  81.         if (itemList == null) {  
  82.             return;  
  83.         }  
  84.         foreach (PoolItem poolI in itemList.Values) {  
  85.             poolI.Destory ();  
  86.         }  
  87.         itemList = null;  
  88.     }  
  89.   
  90.   
  91.   
  92. }  


上面的代码可以直接用,用法是:

1:在每次需要Instantiate时先从对象池中获取出来,然后判断获取的是否为空,为空就Instantiate一个,然后放入对象池中管理。

2:每次需要destroy的时候不执行默认destroy而是执行Poolmanager中的没有真正删除的destroy(DestroyActiveObject)。

3:在GameManager的Update中执行超时检测(BeyondTimeObject)。


注意:

1:对象池中获取的对象需要必要的Init操作。

2:真正频繁删除复用的对象才加入对象池管理,否则并不能达到优化目的。

3:上代码没有对数量进行控制,可以自行修改添加。

4:对象池的应用Unity已经有插件可以使用(PoolManager插件),只需要使用功能的同学可以看:http://www.xuanyusong.com/archives/2974

Unity 中的对象池是一种资源管理技术,用于在游戏运行过程中高效地创建、管理和回收对象。对象池的主要目的是减少频繁创建和销毁对象带来的性能开销,尤其是在有大量短期使用对象(如小敌人、项目等)的情况下。 下面是使用 Unity 对象池的基本步骤: 1. 创建对象池:首先,你需要创建一个包含所需对象类型的新对象池。这通常是一个静态类或专用脚本,负责管理对象的生命周期。 ```csharp public class ObjectPool<T> where T : Component { private List<T> poolObjects; private Stack<T> availableObjects; // 初始化方法 public ObjectPool(int initialSize) { poolObjects = new List<T>(); for (int i = 0; i < initialSize; i++) { T obj = Instantiate<T>(); obj.SetActive(false); // 设置对象为非活动状态,直到需要时才激活 poolObjects.Add(obj); } availableObjects = new Stack<T>(poolObjects); } // 获取对象 public T BorrowObject() { if (availableObjects.Count > 0) { T obj = availableObjects.Pop(); obj.SetActive(true); return obj; } else { T obj = Instantiate<T>(); return obj; } } // 归还对象 public void ReturnObject(T obj) { obj.SetActive(false); availableObjects.Push(obj); } } ``` 2. 使用对象池:当你需要一个新对象时,从池中借用一个,用完后记得归还。这样,当对象不再被使用时,它会被放回池而不是直接销毁,以便后续其他地方可能需要它。 ```csharp private ObjectPool<MyObject> objectPool; void Start() { objectPool = new ObjectPool<MyObject>(10); } void Update() { MyObject newObj = objectPool.BorrowObject(); // 使用 newObj ... newObj.gameObject.SetActive(false); // 当对象不再需要时,归还给池子 // 如果对象池已满,考虑创建更多对象 if (objectPool.availableObjects.Count == 0 && poolSize < MaxPoolSize) { // 添加新对象到池中 objectPool.poolObjects.Add(Instantiate<MyObject>()); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值