對(duì)于unity之中的對(duì)象池技術(shù)葱跋,想必大家都有多耳聞,但是覺(jué)得沒(méi)有多大的必要去使用崔步,我剛開始學(xué)unity時(shí)候也是這么想的稳吮,但是后來(lái)我才意識(shí)到,隨著你所做的項(xiàng)目的規(guī)模的擴(kuò)大井濒,你可能需要頻繁的對(duì)某的游戲?qū)ο螅℅ameObject)進(jìn)行實(shí)例化灶似,在銷毀。假設(shè)你在開發(fā)一個(gè)FPS游戲瑞你,玩家酪惭,乃至大量的AI機(jī)器人都需要頻繁的通過(guò)實(shí)例化“子彈”然后在子彈發(fā)生碰撞后銷毀。
下面是我寫的實(shí)例化小兵(協(xié)程)以及銷毀小兵程序(無(wú)對(duì)象池)
IEnumerator Test01(int number)
{
numNO = 0;
while (numNO < number)
{
numNO++;
//每個(gè)怪產(chǎn)生的時(shí)間差
if (boNO > 5)
yield return new WaitForSeconds(numwait / 2);
else
yield return new WaitForSeconds(numwait);
string a = ChooseEnemy(boNO,numNO);
//動(dòng)態(tài)實(shí)例化者甲,Load(“路徑自定春感,但必須在Resources文件夾里”)
GameObject enemy = (GameObject)Resources.Load("Prefabs_Enemy/"+a);
enemy.GetComponent<EnemyContrl>().Pos = GameObject.FindGameObjectWithTag("WayPos");
//print(enemy.GetComponent<EnemyContrl>().Pos.name);
GameObject o;
//如果在飛行怪,則拔高5
if (enemy.tag == "FlyEnemy")
{
o = Instantiate(enemy, transform.position + Vector3.up * 5, transform.rotation);
}
else
o = Instantiate(enemy,transform.position,transform.rotation);
o.transform.parent = transform;
}
}
/// <summary>
/// 選擇怪物種類,儲(chǔ)存動(dòng)態(tài)實(shí)例化需要的文件名
/// </summary>
/// <param name="boNO"></param>波次
/// <param name="numNO"></param>怪次
/// <returns></returns>
string ChooseEnemy(int boNO, int numNO)
{
switch(boNO % 3)
{
case 1:
{
if (numNO < 8)
{
str = "AmurTiger_01";
}
else if (numNO < 10 && numNO >= 8)
{
str = "AmurTiger_02";
}
else
{
str = "AmurTiger_03";
}
break;
}
case 2:
{
if (numNO < 8)
{
str = "GiantBat";
}
else if (numNO < 10 && numNO >= 8)
{
str = "GiantBat_2";
}
else
{
str = "GiantBat_3";
}
break;
}
case 3:
{
if (numNO < 8)
{
print("3-1");
str = "Manticore_01";
}
else if (numNO < 10 && numNO >= 8)
{
print("3-2");
str = "Manticore_02";
}
else
{
str = "Manticore_03";
}
break;
}
case 0:
{
if (numNO < 4)
{
str = "GiantBat_2";
}
else if (numNO < 7 && numNO >= 4)
{
str = "AmurTiger_02";
}
else if (numNO <= 10 && numNO >= 7)
{
str = "Manticore_02";
}
break;
}
}
return str;
}
public void EnemyDead(float Blood)
{
if (Blood <= 0)
{
this.gameObject.GetComponent<Collider>().enabled = false;
Destroy(this.gameObject.GetComponent<Rigidbody>());
//殺怪物獲得金幣
GameData.Instance.money += 10;
Ani.SetBool("Dead", true);
Ani.SetBool("Attack", false);
//agt.SetDestination(this.transform.position);
this.gameObject.GetComponent<NavMeshAgent>().enabled = false;
Timer += Time.deltaTime;
if (Timer >= DestryTime)
{
Destroy(this.gameObject);
}
}
}
當(dāng)然也有可能是使用觸發(fā)器形式等等就不一一舉例子了鲫懒。這么操作雖然方便快捷纺铭,信手拈來(lái)但是這樣做會(huì)占用很多內(nèi)存資源,如果頻繁這樣操作可能會(huì)導(dǎo)致游戲卡頓甚至閃退刀疙。
那么對(duì)象池技術(shù)就在這種情況下被引入了進(jìn)來(lái)舶赔,所謂的對(duì)象池技術(shù)就是我們把用到的對(duì)象在初始化的時(shí)候都放到這個(gè)對(duì)象池中,需要用到時(shí)可以從激活拿來(lái)用谦秧,不需要用時(shí)可以繼續(xù)禁用放回對(duì)象池中竟纳,這樣大大減少因?yàn)榇罅康膶?shí)例化和銷毀帶來(lái)的性能消耗。
1.新建Unity項(xiàng)目疚鲤,首先創(chuàng)建ObjectPoolsManager.cs管理類锥累,利用單例模式封裝對(duì)象池技術(shù)所用的方法,主要是為了方便調(diào)用集歇;代碼如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 對(duì)象池控制管理腳本
/// </summary>
public class ObjectPoolsManager
{
/// <summary>
/// 1.對(duì)象池-列表存儲(chǔ)單個(gè)對(duì)象
/// </summary>
List<GameObject> _poolList;
/// <summary>
/// 2.對(duì)象池-字典存儲(chǔ)對(duì)象
/// </summary>
Dictionary<int,List<GameObject>> _poolDictionary;
private static ObjectPoolsManager _singleton;
public static ObjectPoolsManager GetInstance()
{
if (_singleton == null) {
_singleton = new ObjectPoolsManager ();
}
return _singleton;
}
public ObjectPoolsManager()
{
_poolList=new List<GameObject> ();
_poolDictionary =new Dictionary<int, List<GameObject>>();
}
/// <summary>
/// 實(shí)例化并存儲(chǔ)游戲?qū)ο蟪刂校▎蝹€(gè)對(duì)象)
/// </summary>
/// <param name="gameObject">被創(chuàng)建的游戲?qū)ο?lt;/param>
/// <param name="pos">游戲?qū)ο笪恢?lt;/param>
/// <param name="rotation">游戲?qū)ο笮D(zhuǎn)</param>
public GameObject SetActiveObject(GameObject gameObject,Vector3 pos,Quaternion rotation)
{
if (_poolList.Count>0) {
GameObject result = _poolList [0];
_poolList.Remove (result);
result.SetActive (true);
result.transform.position = pos;
result.transform.rotation = rotation;
return result;
}
return GameObject.Instantiate (gameObject,pos,rotation) as GameObject;;
}
/// <summary>
/// 實(shí)例化并存儲(chǔ)游戲?qū)ο蟪刂校ǘ鄠€(gè)對(duì)象)
/// </summary>
/// <param name="gameObject">被創(chuàng)建的游戲?qū)ο?lt;/param>
/// <param name="pos">游戲?qū)ο笪恢?lt;/param>
/// <param name="rotation">游戲?qū)ο笮D(zhuǎn)</param>
public GameObject SetActiveMutilObject(GameObject gameObject,Vector3 pos,Quaternion rotation)
{
int key = gameObject.GetInstanceID ();
if (_poolDictionary.ContainsKey (key)) {
if (_poolDictionary [key].Count > 0) {
GameObject result = _poolDictionary [key] [0];
result.SetActive (true);
_poolDictionary [key].Remove (result);
result.transform.position = pos;
result.transform.rotation = rotation;
return result;
}
}
GameObject curObj = GameObject.Instantiate (gameObject,pos,rotation) as GameObject;
curObj.name = gameObject.GetInstanceID ().ToString();
return curObj;
}
/// <summary>
/// 隱藏當(dāng)前對(duì)象桶略,并且添加到列表
/// </summary>
/// <param name="gameObject">被創(chuàng)建的游戲?qū)ο?lt;/param>
public void SetDisableObject(GameObject gameObject)
{
gameObject.SetActive (false);
_poolList.Add (gameObject);
}
/// <summary>
/// 隱藏當(dāng)前對(duì)象,并且添加到字典中
/// </summary>
/// <param name="gameObject">被創(chuàng)建的游戲?qū)ο?lt;/param>
public void SetDisableMutilObject(GameObject gameObject)
{
gameObject.SetActive (false);
int key = int.Parse (gameObject.name);
//不包含鍵名則添加進(jìn)字典一條新的儲(chǔ)存單個(gè)對(duì)象的List
if (!_poolDictionary.ContainsKey (key)) {
_poolDictionary.Add (key,new List<GameObject>());
}
//將gameObject添加進(jìn)他所在的List
_poolDictionary[key].Add(gameObject);
}
/// <summary>
/// 延遲隱藏游戲?qū)ο蠡逵睿⑻砑拥搅斜碇? /// </summary>
/// <returns>The disable object.</returns>
/// <param name="gameObject">被創(chuàng)建的游戲?qū)ο?lt;/param>
/// <param name="time">延遲時(shí)間</param>
public IEnumerator IEDisableObject(GameObject gameObject,float time)
{
//協(xié)程:延遲time秒
yield return new WaitForSeconds (time);
GetInstance ().SetDisableObject (gameObject);
}
/// <summary>
/// 延遲隱藏游戲?qū)ο蠹始撸⑻砑拥阶值渲? /// </summary>
/// <returns>The disable object.</returns>
/// <param name="gameObject">被創(chuàng)建的游戲?qū)ο?</param>
/// <param name="time">延遲時(shí)間</param>
public IEnumerator IEDisableMutilObject(GameObject gameObject,float time)
{
//協(xié)程:延遲time秒
yield return new WaitForSeconds (time);
GetInstance ().SetDisableMutilObject (gameObject);
}
}
其中:
字典允許值對(duì)象為null,并且沒(méi)有個(gè)數(shù)限制姑蓝,所以當(dāng)get()方法的返回值為null時(shí)鹅心,可能有兩種情況,一種是在集合中沒(méi)有該鍵對(duì)象纺荧,另一種是該鍵對(duì)象沒(méi)有映射任何值對(duì)象旭愧,即值對(duì)象為null。因此宙暇,在_poolDictionary字典中不應(yīng)該利用get()方法來(lái)判斷是否存在某個(gè)鍵输枯,而應(yīng)該利用containsKey()方法來(lái)判斷
2.接著簡(jiǎn)單創(chuàng)建一個(gè)測(cè)試對(duì)象池類TestObjectPoolsBehaviour.cs,組件直接掛載到場(chǎng)景中即可占贫;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 測(cè)試對(duì)象池腳本
/// </summary>
public class TestObjectPoolsBehaviour : MonoBehaviour {
public GameObject TestObj;
public GameObject[] TestObjSet;
public float DelayTime = 3;
void Update ()
{
if (Input.GetKeyDown(KeyCode.A)) {
//單個(gè)游戲?qū)ο? GameObject obj = ObjectPoolsManager.GetInstance ().SetActiveObject (TestObj,Vector3.zero,TestObj.transform.rotation) as GameObject;
StartCoroutine (ObjectPoolsManager.GetInstance().IEDisableObject(obj,DelayTime));
}
if (Input.GetKeyDown(KeyCode.B)) {
//多個(gè)游戲?qū)ο? //實(shí)例化
GameObject obj = ObjectPoolsManager.GetInstance ().SetActiveMutilObject (TestObjSet[0],Vector3.zero,TestObjSet[0].transform.rotation) as GameObject;
//延遲隱藏
StartCoroutine(ObjectPoolsManager.GetInstance().IEDisableMutilObject(obj,DelayTime));
}
if (Input.GetKeyDown(KeyCode.C)) {
//多個(gè)游戲?qū)ο? //實(shí)例化
GameObject obj = ObjectPoolsManager.GetInstance ().SetActiveMutilObject (TestObjSet[1],Vector3.zero,TestObjSet[1].transform.rotation) as GameObject;
//延遲隱藏
StartCoroutine(ObjectPoolsManager.GetInstance().IEDisableMutilObject(obj,DelayTime));
}
}
}
這樣的話桃熄,在我們大量,頻繁使用某游戲?qū)ο髸r(shí)靶剑,就可以不必消耗大量?jī)?nèi)存來(lái)頻繁的實(shí)例化再銷毀蜻拨,完全可以,用對(duì)象池桩引,使用SetActive的true,false來(lái)“實(shí)現(xiàn)實(shí)例化與銷毀”缎讼。
不單再FPS游戲中,在塔防游戲里坑匠,也會(huì)用到對(duì)象池技術(shù)血崭。下面是我做的一個(gè)塔防游戲Demo,雖然看起來(lái)很low,但是重點(diǎn)是學(xué)習(xí)不是嗎夹纫。