遊戲設計模式——Unity對象池

對象池這個名字聽起來好像不明覺厲,其實就是將一系列須要反覆建立和銷燬的對象存儲在一個看不到的地方,下次用一樣的東西時往這裏取,相似於一個存放備用物質的倉庫。html

它的好處就是避免了反覆實例化個體的運算,能減小大量內存碎片,固然你須要更多的空間來存這些備用對象,相信使用這些空間是很是值得的。ide

最多見的應用就是子彈的建立和銷燬。this

 

通常對象池都是一個全局性的通用腳本,能夠採用單例模式來設計。spa

http://www.javashuo.com/article/p-pseccouf-gs.html設計

 

對象池至少包含如下兩個基本功能:code

1.從池中取出指定類型的對象orm

2.回收各式各樣的對象到池中htm

 

先定義對象池和池子的容量:對象

1     private const int maxCount = 128;
2     private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>();

容量是一個常量,最好取二的冪值,這樣的話能夠恰好佔用全部內存位的資源,避免浪費。blog

這裏池子用字典標識,key爲對象的名字,這樣比較好記,你用InstanceID也沒問題。

每一個一樣的對象通常在池子中能夠有不少,用一個List來存。

 

下面先定義回收對象的方法:

 1     public void RecycleObj(GameObject obj)
 2     {
 3         var par = Camera.main;
 4         var localPos = obj.transform.localPosition;
 5         obj.transform.SetParent(par.transform);
 6         obj.transform.localPosition = localPos;
 7         obj.SetActive(false);
 8 
 9         if (pool.ContainsKey(obj.name))
10         {
11             if (pool[obj.name].Count < maxCount)
12             {
13                 pool[obj.name].Add(obj);
14             }
15         }
16         else
17         {
18             pool.Add(obj.name, new List<GameObject>() { obj });
19         }
20     }

這裏將回收的對象統一放在了場景主攝像機下,你也能夠選擇放在本身喜歡的位置。

回收對象就是先把對象隱藏,而後看池子中有沒有這一類對象,有的話沒有超過容量上限就直接扔進去。

若是沒有這類對象,那就建立這一類型對象的Key值(名字:好比說螃蟹),順便添加第一隻螃蟹。

 

常常會遇到要批量回收進池子的狀況:

1     public void RecycleAllChildren(GameObject parent)
2     {
3         for (; parent.transform.childCount > 0;)
4         {
5             var tar = parent.transform.GetChild(0).gameObject;
6             RecycleObj(tar);
7         }
8     }

 

對象能夠回收了,那怎麼取呢,天然也是能從池子裏取就從池子裏取,實在不行纔去實例化:

 1     public GameObject GetObj(GameObject perfab)
 2     {
 3         //池子中有
 4         GameObject result = null;
 5         if (pool.ContainsKey(perfab.name))
 6         {
 7             if (pool[perfab.name].Count > 0)
 8             {
 9                 result = pool[perfab.name][0];
10                 result.SetActive(true);
11                 pool[perfab.name].Remove(result);
12                 return result;
13             }
14         }
15         //池子中缺乏
16         result = Object.Instantiate(perfab);
17         result.name = perfab.name;
18         RecycleObj(result);
19         GetObj(result);
20         return result;
21     }

若是池子中有對象,取出來以後記得要把這個對象從該類對象的列表中移除,否則下次可能又會取到這傢伙,而這傢伙已經要派去作別的了。

若是池子中缺乏對象,那就只能實例化了,要注意把實例化後的對應改成你們都同樣的名字,這樣方便下一次取能找到它。

沒有對象的狀況下,我這裏又從新回收了一下再取一次,你也能夠直接返回該對象,至關於在取的時候不存在這類對象的話我提早作了標記。

 

和Instantiate方法同樣,加一個能夠設置父對象的重載方法:

1     public GameObject GetObj(GameObject perfab, Transform parent)
2     {
3         var result = GetObj(perfab);
4         var localPos = result.transform.localPosition;
5         result.transform.SetParent(parent);
6         result.transform.localPosition = localPos;
7         return result;
8     }

 

下面是完整腳本:

 1 using System.Collections.Generic;
 2 using UnityEngine;
 3 
 4 public class ObjectPool:Singleton<ObjectPool>
 5 {
 6     private const int maxCount = 128;
 7     private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>();
 8 
 9     public GameObject GetObj(GameObject perfab)
10     {
11         //池子中有
12         GameObject result = null;
13         if (pool.ContainsKey(perfab.name))
14         {
15             if (pool[perfab.name].Count > 0)
16             {
17                 result = pool[perfab.name][0];
18                 result.SetActive(true);
19                 pool[perfab.name].Remove(result);
20                 return result;
21             }
22         }
23         //池子中缺乏
24         result = Object.Instantiate(perfab);
25         result.name = perfab.name;
26         RecycleObj(result);
27         GetObj(result);
28         return result;
29     }
30 
31     public GameObject GetObj(GameObject perfab, Transform parent)
32     {
33         var result = GetObj(perfab);
34         var localPos = result.transform.localPosition;
35         result.transform.SetParent(parent);
36         result.transform.localPosition = localPos;
37         return result;
38     }
39 
40     public void RecycleObj(GameObject obj)
41     {
42         var par = Camera.main;
43         var localPos = obj.transform.localPosition;
44         obj.transform.SetParent(par.transform);
45         obj.transform.localPosition = localPos;
46         obj.SetActive(false);
47 
48         if (pool.ContainsKey(obj.name))
49         {
50             if (pool[obj.name].Count < maxCount)
51             {
52                 pool[obj.name].Add(obj);
53             }
54         }
55         else
56         {
57             pool.Add(obj.name, new List<GameObject>() { obj });
58         }
59     }
60 
61     public void RecycleAllChildren(GameObject parent)
62     {
63         for (; parent.transform.childCount > 0;)
64         {
65             var tar = parent.transform.GetChild(0).gameObject;
66             RecycleObj(tar);
67         }
68     }
69 
70     public void Clear()
71     {
72         pool.Clear();
73     }
74 }

 

由於是用名字做爲存儲的Key值,因此不一樣類的物體命名不能相同,否則可能會取錯對象。

另外因爲上面的腳本有更改父物體的狀況,可能會出現物體的縮放發生變化,在取出物體以後也能夠對transform進行歸位:

1     public static void ResetLocal(this Transform transform)
2     {
3         transform.localPosition = Vector3.zero;
4         transform.localRotation = Quaternion.identity;
5         transform.localScale = Vector3.one;
6     }

上面是對Transform類的一個擴展方法,例如:

1  var ins = ObjectPool.Instance.GetObj(bulletPrefab, parent.transform);
2  ins.transform.ResetLocal();
相關文章
相關標籤/搜索