物件池

有時當你製作遊戲時,你需要一遍又一遍地建立和銷燬大量相同型別的物件。你可以通過製作預製件並在需要時例項化/銷燬它來執行此操作,但是,執行此操作效率低下並且可能會降低你的遊戲速度。

解決此問題的一種方法是物件池。基本上這意味著你有一個池(有或沒有數量的限制)你將要重用的物件,只要你可以防止不必要的例項化或破壞。

下面是一個簡單物件池的示例

public class ObjectPool : MonoBehaviour 
{
    public GameObject prefab;
    public int amount = 0;
    public bool populateOnStart = true;
    public bool growOverAmount = true;

    private List<GameObject> pool = new List<GameObject>();

    void Start() 
    {
        if (populateOnStart && prefab != null && amount > 0) 
        {
            for (int i = 0; i < amount; i++) 
            {
                var instance = Instantiate(Prefab);
                instance.SetActive(false);
                pool.Add(instance);
            }
        }
    }

    public GameObject Instantiate (Vector3 position, Quaternion rotation) 
    {
        foreach (var item in pool) 
        {
            if (!item.activeInHierarchy) 
            {
                item.transform.position = position;
                item.transform.rotation = rotation;
                item.SetActive( true );
                return item;
            }
        }

        if (growOverAmount) 
        {
            var instance = (GameObject)Instantiate(prefab, position, rotation);
            pool.Add(instance);
            return instance;
        }

        return null;
    }
}

我們先來看看變數

public GameObject prefab;
public int amount = 0;
public bool populateOnStart = true;
public bool growOverAmount = true;

private List<GameObject> pool = new List<GameObject>();
  • GameObject prefab:這是物件池用於將新物件例項化到池中的預製件。
  • int amount:這是池中可以包含的最大專案數。如果要例項化另一個專案並且池已達到其限制,則將使用池中的另一個專案。
  • bool populateOnStart:你可以選擇在開始時填充池。這樣做會使預製件的例項填滿游泳池,這樣當你第一次呼叫 Instantiate 時,你將獲得一個已經存在的物件
  • bool growOverAmount:將此設定為 true 允許池在超過特定時間範圍內請求的數量時增長。你並不總是能夠準確預測要放入池中的專案數量,因此這將在需要時向池中新增更多內容。
  • List<GameObject> pool:這是池,儲存所有例項化/銷燬物件的地方。

現在讓我們看看 Start 功能

void Start() 
{
    if (populateOnStart && prefab != null && amount > 0) 
    {
        for (int i = 0; i < amount; i++) 
        {
            var instance = Instantiate(Prefab);
            instance.SetActive(false);
            pool.Add(instance);
        }
    }
}

在啟動函式中,我們檢查是否應該在開始時填充列表,如果已經設定了 prefab 並且金額大於 0(否則我們將無限制地建立)。

這只是一個簡單的 for 迴圈例項化新物件並將它們放入池中。要注意的一件事是我們將所有例項設定為非活動狀態。這樣他們在遊戲中就看不到了。

接下來,有 Instantiate 函式,這是大多數魔法發生的地方

public GameObject Instantiate (Vector3 position, Quaternion rotation) 
{
    foreach (var item in pool) 
    {
        if (!item.activeInHierarchy) 
        {
            item.transform.position = position;
            item.transform.rotation = rotation;
            item.SetActive(true);
            return item;
        }
    }

    if (growOverAmount) 
    {
        var instance = (GameObject)Instantiate(prefab, position, rotation);
        pool.Add(instance);
        return instance;
    }

    return null;
}

Instantiate 功能看起來就像 Unity 自己的 Instantiate 功能,除了預製件已作為類成員提供。

Instantiate 函式的第一步是檢查池中是否存在非活動物件。這意味著我們可以重用該物件並將其返回給請求者。如果池中有一個非活動物件,我們設定位置和旋轉,將其設定為活動(否則如果你忘記啟用它可能會被意外重用)並將其返回給請求者。

第二步只有在池中沒有非活動項且允許池增長超過初始量時才會發生。發生的事情很簡單:建立預製件的另一個例項並將其新增到池中。允許池的增長可以幫助你在池中擁有適量的物件。

第三個只如果在池中沒有不活動的專案情況和池讓它生長。當發生這種情況時,請求者將收到一個空 GameObject,這意味著什麼都沒有,應該正確處理以防止 NullReferenceExceptions

重要!

為了確保你的專案得到放回池中,你應該破壞遊戲的物件。你唯一需要做的就是將它們設定為非活動狀態,這樣可以通過池重新使用它們。