為了優化項目資源,我們在場景中最好使用對象池來儲存子彈這種需要不斷實例出來的對象;因為像子彈這種對象,如果實例出來,用Destroy來銷毀后它其實還是占用內存資源的,這些被占用的內存在下下個場景加載完成后才會清空,所以我們要把這些能夠重復利用的對象放到對象池中,當使用時把它的SetActive設為true,不使用時設為false,這樣就避免不斷地重復實例和銷毀對象。
對象池的特點:
1.節省對象重復創建和初始化所耗費的時間
2.簡化對象獲取和使用的過程
3.優化內存資源,提高系統性能
下面就來用一個模擬子彈彈夾來簡單介紹對象池的使用。
先創建一個新場景,里面創建一個地面Plane和一個方塊Cube,用Cube來模擬槍(雖然很low,就將就著用,這不是重點= =),如圖:
Paste_Image.png
然后再創建一個子彈(其實就是一個Sphere)預設體,如圖:
Paste_Image.png
(記得改一下Sphere的Scale值為0.2)
這個時候我們就可以先寫一個對象池的腳本:
public class GameObjectPool : MonoBehaviour
{
const int number = 10;//子彈彈夾的容量
bool isin = true;//用來判斷是否實例子彈對象
int n;//用來記錄實例出子彈對象的個數
//集合里面的元素,相當于對象池里面的對象,這里的集合可看作為對象池。
static List<GameObject> pools = new List<GameObject>();
//再聲明一個集合,用來回收發射出去的子彈對象
List<GameObject> pb = new List<GameObject>();
//首先創建一個單例
private GameObjectPool() { }
private static GameObjectPool instance;
public static GameObjectPool GetInstance(GameObject name)//此處的Gameobject name就是需要放入對象池的對象--子彈
{
if (instance == null)
{
print("實例池子");
//動態的生成一個名為“GameObjectPool”的對象并將單例腳本附加上去
instance = new GameObject("GameObjectPool").AddComponent<GameObjectPool>();
for (int i = 0; i < number; i++)//先往對象池中放入十發子彈
{
name.SetActive(false);
pools.Add(name);
}
}
return instance;
}
//從對象池中取對象
public GameObject MyInstantiate()
{
if (pools.Count == 0)//如果十發子彈打完
{
print("沒子彈了:" + pools.Count);
return null;
}
else
{
// print(pools[0].name);
//取出對象池里面的第一個元素
GameObject obj= pools[0];
if (isin)//判斷是否已經實例了十發子彈
{
//obj = pools[0];
Instantiate(obj);//實例出子彈
n++;//計數加加
if (n == number)
{
isin = false;
}
}
//將對象設置為激活狀態
obj.SetActive(true);
//將被取出的元素,從對象池中移除
pools.Remove(obj);
return obj;
}
}
//回收子彈的方法
public void MyDisable(GameObject name)
{
//將傳進來的對象隱藏(處于非激活狀態)
name.SetActive(false);
//回收子彈添加到pb集合中
pb.Add(name);
}
public void ResetBullet()//換彈夾的方法
{
if (pb.Count != 0)//判斷回收子彈的集合是否是空
{
print("pb不是空");
for (int i = 0; i < pb.Count; i++)
{
pools.Add(pb[i]); //把回收的子彈重新加到彈夾中去
}
pb.Clear();
}
}
}
這個腳本不需要掛到任何物體上;
然后我們需要給預設體子彈寫一個腳本:
public class Bullet : MonoBehaviour {
void OnEnable()
{
//開啟協程
StartCoroutine(DelayDisable(1f));
}
void Update()
{
//子彈自動向前運動
transform.Translate(Vector3.forward * Time.deltaTime * 50);
}
void OnDisable()
{
Debug.Log("I'm over");
}
//子彈消失的方法
IEnumerator DelayDisable(float time)
{
//等待一秒
yield return new WaitForSeconds(time);
//調用單例中向集合pb里面回收子彈對象的方法
GameObjectPool.GetInstance(gameObject).MyDisable(gameObject);
}
}
如圖
Paste_Image.png
最后我們給Cube加一個腳本,用來控制它發射子彈
public class GameManerge : MonoBehaviour {
//創建子彈的預設體
public GameObject mBulletPrefab;
void Update()
{
//如果按下鼠標左鍵,發射子彈
if (Input.GetMouseButtonDown(0))
{
//調用單例腳本里面的從對象池中取對象的方法
GameObject go = GameObjectPool.GetInstance(mBulletPrefab).MyInstantiate();
if(go!=null)
{
go.transform.position = transform.position; //設置子彈生成的位置是Cube的位置
}
}
//按下鼠標右鍵,換彈夾
if (Input.GetMouseButtonDown(1))
{
print("換子彈:");
GameObjectPool.GetInstance(mBulletPrefab).ResetBullet();
}
}
}
如圖,
Paste_Image.png
這樣就運用對象池做好了一個彈夾效果,場景有點丑但達到的效果還是不錯的^_