Unity編程標(biāo)準(zhǔn)導(dǎo)引-3.4 Unity中的對(duì)象池
本節(jié)通過(guò)一個(gè)簡(jiǎn)單的射擊子彈的示例來(lái)介紹Transform的用法。子彈射擊本身很容易制作,只要制作一個(gè)子彈Prefab,再做一個(gè)發(fā)生器,使用發(fā)生器控制按頻率產(chǎn)生子彈,即克隆子彈Prefab,然后為每個(gè)子彈寫(xiě)上運(yùn)動(dòng)邏輯就可以了。這本該是很簡(jiǎn)單的事情。不過(guò)問(wèn)題來(lái)了,發(fā)射出去后的子彈如何處理?直接Destroy嗎?這太浪費(fèi)了,要知道Unity的Mono內(nèi)存是不斷增長(zhǎng)的。就是說(shuō)出了Unity內(nèi)部的那些網(wǎng)格、貼圖等等資源內(nèi)存(簡(jiǎn)單說(shuō)就是繼承自UnityEngine下的Object的那些類(lèi)),而我們自己寫(xiě)的C#代碼繼承自System下的Object,這些代碼產(chǎn)生的內(nèi)存即是Mono內(nèi)存,它只增不減。同樣,你不斷Destroy你的Unity對(duì)象也是要消耗性能去進(jìn)行回收,而子彈這種消耗品實(shí)在產(chǎn)生的太快了,我們必需加以控制。
那么,我們?nèi)绾慰刂剖沟貌恢劣诓粩喈a(chǎn)生新的內(nèi)存呢?答案就是自己寫(xiě)內(nèi)存池。自己回收利用之前創(chuàng)建過(guò)的對(duì)象。所以這個(gè)章節(jié)的內(nèi)容,我們將重點(diǎn)放在寫(xiě)一個(gè)比較好的內(nèi)存池上。就我自己來(lái)講,在寫(xiě)一份較為系統(tǒng)的功能代碼之前,我考慮的首先不是這個(gè)框架是該如何的,而是從使用者的角度去考慮,這個(gè)代碼如何寫(xiě)使用起來(lái)才會(huì)比較方便,同樣也要考慮容易擴(kuò)展、通用性強(qiáng)、比較安全、減少耦合等等。
3.4.1、從使用者視角給出需求
首先,我所希望的這個(gè)內(nèi)存池的代碼最后使用應(yīng)該是這樣的。
Bullet a = Pool.Take<Bullet>(); //從池中立刻獲取一個(gè)單元,如果單元不存在,則它需要為我立刻創(chuàng)建出來(lái)。返回一個(gè)Bullet腳本以便于后續(xù)控制。注意這里使用泛型,也就是說(shuō)它應(yīng)該可以兼容任意的腳本類(lèi)型。
Pool.restore(a);//當(dāng)使用完成Bullet之后,我可以使用此方法回收這個(gè)對(duì)象。注意這里實(shí)際上我已經(jīng)把Bullet這個(gè)組件的回收等同于某個(gè)GameObject(這里是子彈的GameObject)的回收。
使用上就差不多是這樣了,希望可以有極其簡(jiǎn)單的方法來(lái)進(jìn)行獲取和回收操作。
3.4.2、內(nèi)存池單元結(jié)構(gòu)
最簡(jiǎn)單的內(nèi)存池形式,差不多就是兩個(gè)List,一個(gè)處于工作狀態(tài),一個(gè)處于閑置狀態(tài)。工作完畢的對(duì)象被移動(dòng)到閑置狀態(tài)列表,以便于后續(xù)的再次獲取和利用,形成一個(gè)循環(huán)。我們這里也會(huì)設(shè)計(jì)一個(gè)結(jié)構(gòu)來(lái)管理這兩個(gè)List,用于處理同一類(lèi)的對(duì)象。