Unity 遊戲物件池 極簡版


建立時間: 2023年5月20日 21:31
更新時間: 2023年5月21日 09:36

說明

物件池是一種設計模式,在遊戲中有很多情況要不斷地產生遊戲物件,過沒多久後又被摧毀,例如:子彈。當大量的遊戲物件被實體化與摧毀導致遊戲出現延遲時,使用物件池的概念可以大幅提升效能,本篇將告訴大家物件池的概念和實作。

什麼是物件池

物件池(Object Pool)是一種軟體設計模式,用於提高物件的重複使用性和效能。它是一個儲存和管理物件的集合,這些物件在一段時間內被預先建立和初始化,並在需要時被重複使用,而不需要頻繁地創建和銷毀物件。

在生活中有很多物件池的例子,例如穿衣服,平常人們會買衣櫥將一些衣物、內衣褲放在衣櫥中,然後選一套穿在身上,你可以把衣櫥當作是池子(Pool),衣櫥裡面的衣物就當作是被預先建立和初始化好的物件,當你想穿衣服時就會從衣櫥拿一套衣服出來穿,髒了就拿去洗一洗,洗完曬乾再放回衣櫥,下次就不用再去買衣服了,可以直接拿衣櫥裡面的衣服穿,但就如現實情況一樣,衣櫥會佔用一點空間。

實作

此版本的物件池是我修改過的極簡版,用途是產生大量的需要重複使用的遊戲物件,遊戲初始化會產生指定數量的遊戲物件,當數量不足時,會自動擴大池子裡面的物件數量,池子容量沒有上限。

你可以依照自己的需求,進一步客製化自己的物件池。

GameObjectPool.cs

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 遊戲物件池
/// </summary>
public class GameObjectPool : MonoBehaviour
{
    /// <summary>
    /// 預製
    /// </summary>
    [SerializeField]
    private GameObject prefab;

    /// <summary>
    /// 遊戲物件池
    /// </summary>
    private List<GameObject> pooledGameObjects;

    /// <summary>
    /// 一開始要產生的數量
    /// </summary>
    [SerializeField]
    private int amountToPool;

    private void Awake()
    {
        pooledGameObjects = new();
    }

    void Start()
    {
        GameObject _gameObject;

        for (int i = 0; i < amountToPool; i++)
        {
            _gameObject = Instantiate(prefab, transform);
            _gameObject.SetActive(false);
            pooledGameObjects.Add(_gameObject);
        }
    }

    /// <summary>
    /// 取得一個物件,若池沒有物件,就會新增一個新的物件到池子當中
    /// </summary>
    /// <returns></returns>
    public GameObject GetPooledGameObject()
    {
        foreach (GameObject pooledGameObject in pooledGameObjects)
        {
            if (!pooledGameObject.activeInHierarchy)
            {
                pooledGameObject.SetActive(true);

                return pooledGameObject;
            }
        }
        GameObject _gameObject = Instantiate(prefab, transform);
        pooledGameObjects.Add(_gameObject);

        return _gameObject;
    }
}

使用方式

以下是一個球池的範例。

建立一個空遊戲物件,新增 GameObjectPool 組件,並設定好屬性。

ball pool

要建立一顆球,可以這樣寫。

GameObjectPool ballPool = GameObject.Find("BallPool").GetComponent<GameObjectPool>();
GameObject ballGameObject = BallPool.GetPooledGameObject();

要回收一顆球,只需 SetActive(false)

ballGameObject.SetActive(false);

較詳細的工作流程,可參考我寫的 Ball 組件使用機制。

Ball.cs

using UnityEngine;

/// <summary>
/// 球
/// </summary>
public class Ball : MonoBehaviour
{
    /// <summary>
    /// 球池
    /// </summary>
    private static GameObjectPool ballPool;

    /// <summary>
    /// 球池
    /// </summary>
    public static GameObjectPool BallPool
    {
        get
        {
            if (ballPool == null)
            {
                ballPool = GameObject.Find("BallPool").GetComponent<GameObjectPool>();
            }

            return ballPool;
        }
    }

    /// <summary>
    /// 建立一顆球的遊戲物件
    /// </summary>
    /// <param name="position">球的位置</param>
    /// <returns></returns>
    public static GameObject CreateOneBall(Vector3 position)
    {
        GameObject ballGameObject = BallPool.GetPooledGameObject();
        ballGameObject.transform.position = position;

        return ballGameObject;
    }
}

上面範例,CreateOneBall 可以產生一顆球並且指定位置,接著就可以在想要建立一顆球時使用靜態的方法獲得一顆球,並且指定位置。

GameObject ballGameObject = Ball.CreateOneBall(transform.position);

回收機制就是當球碰到某個區域時,例如:出界,就將球回收。

DeadZone.cs

using UnityEngine;

/// <summary>
/// 清除出界的遊戲物件
/// </summary>
public class DeadZone : MonoBehaviour
{
    private void OnCollisionEnter2D(Collision2D other)
    {
        if (!other.gameObject.CompareTag("Ball"))
        {
            return;
        }
        other.gameObject.SetActive(false);
    }
}

以上就是整個球的獲取和回收機制。

參考

觀看次數: 1190
gameobjectpoolunity物件池
按讚追蹤 Enjoy 軟體 Facebook 粉絲專頁
每週分享資訊技術

一杯咖啡的力量,勝過千言萬語的感謝。

支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!