什么是對(duì)象池?
對(duì)象池汹胃,簡(jiǎn)單的說(shuō)就是一種為了避免重復(fù)創(chuàng)建婶芭,刪除對(duì)象的解決方案。它可以通過(guò)復(fù)用游戲?qū)ο笞偶ⅲ欢ǔ潭壬咸岣哂螒蛐阅芟苊鈨?nèi)存消耗,特別針對(duì)任何頻繁創(chuàng)建刪除對(duì)象的情景宰掉。這符合了性能優(yōu)化中對(duì)腳本優(yōu)化的理念呵哨。是居家旅行...哦不...游戲開(kāi)發(fā)必備技能赁濒。
對(duì)象池的實(shí)現(xiàn)原理?
創(chuàng)建復(fù)用對(duì)象的集合孟害,通過(guò)查找和設(shè)置集合元素的active狀態(tài)拒炎,達(dá)到和創(chuàng)建、刪除一樣的視覺(jué)效果纹坐。
這里先通過(guò)一個(gè)射擊子彈的簡(jiǎn)單例子枝冀,從不用對(duì)象池,到給子彈創(chuàng)建對(duì)象池耘子,去繁就簡(jiǎn)的一步步呈現(xiàn)對(duì)象池使用過(guò)程果漾,深化理解。
不用對(duì)象池的小白腳本
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);
}
}
這里很容易的腦補(bǔ)出Hierarchry下,bullet對(duì)象一直在創(chuàng)建谷誓,銷(xiāo)毀绒障,創(chuàng)建,銷(xiāo)毀捍歪。户辱。。
使用對(duì)象池的一階腳本
Control.cs
...
using System.Collections.Generic;
public class Control:MonoBehaviour{
public GameObject bulletPrb;
public int poolingCapacity = 20;
public List<GameObject> pool;
void Start(){ //創(chuàng)建對(duì)象池糙臼,并將對(duì)象設(shè)為不可見(jiàn)
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++)
{ //自動(dòng)搜索對(duì)象池中失活的對(duì)象并激活顯示
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); //只要隱藏掉就好庐镐,對(duì)象還在對(duì)象池中
}
void OnDisable{
CancelInvoke();
}
}
這樣對(duì)象池的功能算是實(shí)現(xiàn)了。然而它是被構(gòu)建在控制腳本中的变逃,這樣顯然不行必逆。這就像編程小白把所有語(yǔ)句寫(xiě)在主函數(shù)中一樣,這不符合面向?qū)ο蟮乃枷肜柯遥∫虼嗣迹枰谌?/p>
使用對(duì)象池的二階腳本
新建一個(gè)ObjectBulletPool.cs 并掛載到新建的空物體ObjectPooler上
...
using System.Collections.Generic
public class ObjectPool: MonoBehaviour{
public static ObjectPool current;
public GameObject objectPrb; //將被放入對(duì)象池中的預(yù)制體
public int poolCapacity = 20;
public List<GameObject> pool;
public bool willgrow = true; //池子容量不夠時(shí),是否自動(dòng)擴(kuò)展池子
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++) //遍歷對(duì)象池凰棉,將未激活的對(duì)象傳遞出去
{
if ( ! pool[i].activeInHierarchy )
return pool[i];
}
if ( willgrow ) //當(dāng)池子中所有對(duì)象都激活了损拢,但是還想激活顯示對(duì)象時(shí),擴(kuò)展池子
{
GameObject obj = Instantiate(objectPrb);
pool.Add(obj);
obj.SetActive(false);
return obj;
}
return null;
}
}
簡(jiǎn)化 Control.cs
...
using System.Collections.Generic;
public class Control:MonoBehaviour{
/*
public GameObject bulletPrb;
public int poolingCapacity = 20;
public List<GameObject> pool;
void Start(){ //創(chuàng)建對(duì)象池撒犀,并將對(duì)象設(shè)為不可見(jiàn)
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++)
{ //自動(dòng)搜索對(duì)象池中失活的對(duì)象并激活顯示
if ( !pool[i].activeinHierarchy )
{
pool[i].transform.position = transform.position;
pool[i].transform.rotation = transform.rotation;
pool[i].SetActive(true);
break;
}
}
*/
//獲取未激活對(duì)象福压,激活顯示
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); //只要隱藏掉就好,對(duì)象“回到”對(duì)象池中
}
void OnDisable{
CancelInvoke();
}
}
多說(shuō)一句或舞,如果你能發(fā)射不同類(lèi)型的子彈怎么辦隧膏?
解決思路:將子彈類(lèi)型bulletPrb對(duì)象改成數(shù)組,在GetPooledObject()函數(shù)中傳入子彈類(lèi)型的參數(shù)嚷那,然后遍歷數(shù)組匹配子彈類(lèi)型,return出去杆煞。
小結(jié):
這個(gè)對(duì)象池其實(shí)是固定的魏宽,一旦創(chuàng)建就不用刪除腐泻,這樣便于復(fù)用創(chuàng)建好的對(duì)象。使用對(duì)象其實(shí)就是在對(duì)象池中搜索沒(méi)有active的對(duì)象并激活獲取到它好進(jìn)行操作队询。釋放對(duì)象就是讓他失活派桩,好被以后搜索復(fù)用嬉探。
Ok,看到這里年栓,對(duì)象池的基本用法就掌握了,在實(shí)際項(xiàng)目中需求會(huì)更加多變滔灶,但是萬(wàn)變不離本質(zhì)送膳。高階運(yùn)用也是從低階進(jìn)化的员魏!
高階運(yùn)用請(qǐng)看這位老兄的文章:unity中的通用對(duì)象池