Unity GameObject 對象池

  對象池是遊戲開發中經常使用的優化方法。編程

  解決問題:在某些類型的遊戲,相同的對象會屢次建立和銷燬,這些對象的建立十分耗時,於是,咱們會以一部份內存爲代價,將這部分對象緩存起來,並不去銷燬它,在須要建立時,從緩存中將先前建立好的對象取出來使用。緩存

  在Unity遊戲開發中,建立GameObject是一個費時的過程,本文將針對GameObject類建立一個對象池。由於是一個很是經常使用的優化手段,於是,咱們須要把其可以方便的移植到其餘項目中,不妨放到一個單一的經常使用工具的文件夾中,而後能夠很方便的導出爲package,並在其餘項目中導入引用。函數

public class PoolOfGameObjects
{
  private List<GameObject> pool;//存放提早建立好的對象緩存區
  private GameObject sample;//這個對象池緩存的對象樣本
  private int index;//未使用對象的位置座標(好比10個對象,使用了3個,index就會變成3,即pool[3]是下一個可用得對象)
}工具

  首先,咱們須要去建立這個池,即將它的屬性初始化:性能

public PoolOfGameObjects(GameObject sample)
{
  this.sample = sample;//無論怎麼說,你都先要給對象池一個對象作樣本,否則池子裏該放什麼呢?
  pool = new List<GameObject> ();//把池子初始化,否則你提早建立好的對象要放哪裏呢?
  index = -1;//如今池子裏是空的,因此你讀取pool[-1]只會獲得錯誤。
}測試

  接下來咱們複製一些樣本的副本到池子裏:優化

private void Add(int count=1)
{
  for (int i = 0; i < count; i++)
  {
    GameObject copy = GameObject.Instantiate (this.sample);
    copy.SetActive (false);//還沒使用的物體就讓它安靜躺着池子裏,否則可能會被渲染出來。
    pool.Add (copy);
  }
}this

這個方法是私有的,咱們的池子很智能,不須要外部給咱們加水,當池子的對象被耗盡,池子會本身往裏添加更多能用的對象。線程

  接下來,咱們應該在何時往池裏加對象呢?也許你的遊戲有一個漂亮的加載界面,你也許想在這時候給池子里加上差很少夠用的對象備用,也許你沒有那麼多內存去存放多餘的對象,你甚至不清楚你會用到幾個,或者你並無足夠的加載時間,只想吝嗇的須要一個時添加一個(在編程時,但願你能作一個吝嗇鬼)。對象

  無論你是前者仍是後者,既然想讓更多的項目可以複用,咱們得能同時知足二者的須要,爲了前者,咱們須要另外一個構建函數:

public PoolOfGameObjects(GameObject sample,int capacity)
{
  this.sample = sample;
  pool = new List<GameObject> ();
  Add(capacity);
  index = 0;
}

在初始化池子的時候,咱們就往裏面建立了用戶預計會使用到的對象副本個數。

 

  如今,你有了一個池子,也許裏面還有一些對象供你使用,如今來規定一下你能用這個池子來作什麼,首先,這個池子裏的對象是供咱們拿出來使用的,而且若是你有良好的管理習慣,你應該認爲,用完再放回原處是理所固然的,咱們必定要吝嗇,若是你隨意丟棄,那麼當你把池子裏的對象用盡時,不得再也不請求cpu再爲你建立一個,每建立一個副本,池子的體積就會愈來愈大,你可能認爲,我已經把GameObject清理掉了,但池子並不會知道你以及把它的管理對象清理了,它仍是會爲對象保留一個位置。因此咱們須要兩個基本方法,借用和歸還。

  首先是借用:

public GameObject Borrow()
{
  if (index >= 0 && index < pool.Count)//index屬性保存着可用對象的下標,我但願能連續的取對象,而不是每次都去循環一遍List,假如100箇中用掉99個,得須要循環多少次哦
  {
    pool[index].SetActive(true);
    return pool[index++];//借出一個後,浮標移動到僅靠着得下一位
  }
  else
  {
    Add();//不夠得時候,得爲池子增長一個對象
    if (index < 0)
    {
      index = 0;//這裏用index++也能夠
    }
    return Borrow(); //剛剛沒有借到,如今增長了一個對象,總能夠借給我了
  }
}

  有時候你須要一次性借多個,你總不但願要來好多趟吧:

public GameObject[] Borrow(int count)
{
  GameObject[] order = new GameObject[count];
  for (int i = 0; i < count; i++)//不要介意這裏使用的循環,假如你把這個循環放到裏層,建立GameObject的時間複雜度是同樣的,但會多調用幾回上面的方法,這裏我不想爲這一點性能,多寫一部分代碼。
  {
    order[i] = Borrow();
  }
  return order;
}

  使用完以後,咱們還要好好的把副本還回去:

public bool Lend(GameObject gameobject)
{
  for (int i = 0; i < index; i++)//只須要在已經借出的列表前部分進行比對
  {
    if (pool[i].Equals(gameobject))//的確是這個池子裏的借出去的對象
    {
      pool[i].SetActive(false);
      pool.Insert(pool.Count, pool[i]);//將對象插入到最後面待以後繼續使用
      pool.Remove(pool[i]);//將原來的空出來的位置去掉
      index--;//浮標向前一位
      return true;//歸還成功
    }
  }
  return false;//歸還不成功
}

  一樣的,歸還你也不想重複好幾回吧:

public GameObject[] Lend(GameObject[] gameobects)
{
  List<GameObject> notMatch = new List<GameObject>();
  for (int i = 0; i < gameobects.Length; i++)
  {
    if (!Lend(gameobects[i]))
    {
      notMatch.Add(gameobects[i]);
    }
  }
  return notMatch.ToArray();
}

歸還多個的變數多一些,咱們將不匹配的對象拒退給用戶。

  最後,咱們還須要有清理對象池的方法,有時候咱們沒有把握好初始化池子的大小,或者一開始咱們用了不少副本,可是以後咱們須要的不多,將未使用的副本清理出去:

public void Clear()
{
  if (index >= 0)
  {
    for (int i = pool.Count-1; i >=index; i--)
    {
      if (!pool[i].activeSelf)//以防咱們刪掉正在使用的對象,這本是沒有必要的。通過測試,有沒有這個判斷不會形成誤刪,可是多一層保險,有時候未必是壞事
      {
        GameObject.Destroy(pool[i]);
      }
    }
    pool.RemoveRange(index, pool.Count - index);//把池子的容量恢復到恰好的尺寸
  }
}

  當你不想要這個池子的時候,須要銷燬它來釋放更多的內存:

public void Destory(bool force)//false時,僅僅銷燬池子和未使用的對象,已經使用的對象不會被銷燬,但也沒法再歸還回來;true時,已經使用的對象也會被強制銷燬掉。
{
  int start;
  if (force)
  {
    start = 0;
  }
  else
  {
    start = index;
  }
  for (int i = pool.Count - 1; i >= start; i--)
  {
    if ((force) || (!pool[i].activeSelf))
    {
      GameObject.Destroy(pool[i]);
    }
  }
  pool.Clear();
}

  以上,就是一個GameObject對象池的基本實現,放心大膽的部署在你的各個Unity應用中,也許你還會碰到各類池,好比常見的線程池,整體的思路都是如此,具體實現會略有不一樣,你還能夠建立一個統一的接口,添加各有特點的池,讓你的池系統更加完善

相關文章
相關標籤/搜索