目次
1 オブジェクト プーリングの概要
Unity のオブジェクト プーリングは、ゲームのパフォーマンスを向上させ、リソースの無駄を削減するための最適化スキームです。特に、一部のシューティング ゲームで弾を発射するときなど、ゲーム オブジェクトをすばやく作成および破棄する必要がある場合に特に便利です。
オブジェクト プールは、ゲームを実行する前に一定数のオブジェクトを事前に作成し、それらを再利用辞書 (または配列) に保存し、必要な場合にのみ必要なゲーム オブジェクトをアクティブ化または非アクティブ化します。実際、オブジェクトを使用するのではなく、オブジェクトをリサイクルするだけです。ネイティブの Instantiate メソッドと Destroy メソッドは、オブジェクトを作成および破棄します。すべてのアクティブなオブジェクトと非アクティブなオブジェクトのこのディクショナリ (または配列) をプールと呼びます。これにより、頻繁にオブジェクトを作成および破棄するオーバーヘッドが大幅に削減され、メモリ割り当てとガベージ コレクションが削減され、ゲームの実行効率が向上します。
2 オブジェクト プール スクリプトを実装する
シリアル化されたゲーム オブジェクト プレハブを使用してオブジェクト プール スクリプト ObjectPool.cs を実装し、同じタイプの多数のオブジェクトを迅速に生成してリサイクルします。
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 此类实现了对象池模式,用于创建和管理大量的游戏对象
/// </summary>
public class ObjectPool
{
private readonly GameObject prefab; // 预制体对象,用来创建新的游戏对象
private readonly int initialPoolSize; // 初始对象池大小,即预先生成的游戏对象数量
private readonly bool canGrow; // 是否可以动态扩展对象池大小
private readonly List<GameObject> pool = new List<GameObject>(); // 游戏对象池
// 单例对象,用于全局访问对象池
public static ObjectPool Instance;
// 私有无参构造函数,避免从外部创建对象池实例
private ObjectPool()
{
this.prefab = null;
this.initialPoolSize = 0;
this.canGrow = false;
}
// 构造函数,创建对象池实例,并初始化预制体对象、对象池大小、是否可扩展等参数
public ObjectPool(GameObject prefab, int initialPoolSize, bool canGrow)
{
this.prefab = prefab;
this.initialPoolSize = initialPoolSize;
this.canGrow = canGrow;
InitializePool(); // 初始化对象池
}
// 从对象池中获取未被使用的游戏对象,如果对象池已满并且可以扩展,将会创建新的游戏对象
public GameObject GetObject()
{
GameObject obj = pool.Find(o => !o.activeSelf); // 查找未被激活的游戏对象
if (obj == null && canGrow) // 如果对象池满了并且可以扩展
{
obj = AddObjectToPool(); // 创建新的游戏对象
}
if (obj != null)
{
obj.SetActive(true); // 激活游戏对象
}
return obj;
}
// 将游戏对象返回到对象池中
public void ReturnObject(GameObject obj)
{
obj.SetActive(false); // 取消游戏对象的激活状态
}
// 初始化对象池,创建一定数量的游戏对象并加入到游戏对象池中
private void InitializePool()
{
for (int i = 0; i < initialPoolSize; i++)
{
GameObject obj = GameObject.Instantiate(prefab); // 创建新的游戏对象
obj.SetActive(false); // 初始时将游戏对象设置为未激活状态
pool.Add(obj); // 将游戏对象加入到对象池中
}
}
// 向对象池中添加新的游戏对象
private GameObject AddObjectToPool()
{
GameObject obj = GameObject.Instantiate(prefab); // 创建新的游戏对象
obj.SetActive(false); // 初始时将游戏对象设置为未激活状态
pool.Add(obj); // 将游戏对象加入到对象池中
return obj;
}
}
3 オブジェクトプールを使用して Cube を生成する
上記のオブジェクト プールを使用して、マウスの左ボタンをクリックした位置に Cube を生成するスクリプト NewCube.cs を記述し、マウスの右ボタンをクリックして以前に生成された Cube を 1 つずつ破棄し、キーボードの X キーを押します。すべてのキューブを破壊します。
using System.Collections.Generic;
using UnityEngine;
public class NewCube : MonoBehaviour
{
[SerializeField]
private GameObject cubePrefab; // Cube预制体
[SerializeField]
private float spawnHeight = 10f; // 在鼠标落下的位置生成Cube时使用的高度值
[SerializeField]
private int initialPoolSize = 20; // 初始化对象池时的初始大小
[SerializeField]
private bool canGrow = true; // 当对象池中的对象数量达到最大值时,canGrow 为 true 的话会增加对象池的大小
private GameObject currentCube; // 当前的Cube游戏对象
private Vector3 lastCubePosition; // 上一个Cube的位置
private List<GameObject> cubesList = new List<GameObject>(); // 用于记录所有的Cube
private void Start()
{
ObjectPool.Instance = new ObjectPool(cubePrefab, initialPoolSize, canGrow);
}
private void Update()
{
if (Input.GetMouseButtonDown(0)) // 鼠标左键按下
{
SpawnCube();
}
else if (Input.GetMouseButtonDown(1)) // 鼠标右键按下
{
DeleteLastCube();
}
if (Input.GetKeyDown(KeyCode.X)) // X键按下
{
ClearCubes();
}
}
//生成Cube对象
private void SpawnCube()
{
Vector3 mousePos = Input.mousePosition;
mousePos.z = spawnHeight;
Vector3 spawnPos = Camera.main.ScreenToWorldPoint(mousePos);
if (currentCube != null)
{
lastCubePosition = currentCube.transform.position;
}
currentCube = ObjectPool.Instance.GetObject();
currentCube.transform.position = spawnPos;
currentCube.SetActive(true);
Rigidbody rb = currentCube.GetComponent<Rigidbody>();
rb.velocity = Vector3.zero;
rb.useGravity = true;
lastCubePosition = currentCube.transform.position;
cubesList.Add(currentCube); // 将当前Cube添加到列表中
}
// 删除上一个生成的 Cube 对象
private void DeleteLastCube()
{
if (cubesList.Count > 0)
{
ObjectPool.Instance.ReturnObject(cubesList[cubesList.Count - 1]);
cubesList.RemoveAt(cubesList.Count - 1); // 从列表中删除最后一个Cube
}
if (cubesList.Count > 0)
{
lastCubePosition = cubesList[cubesList.Count - 1].transform.position;
}
else
{
lastCubePosition = Vector3.zero;
}
}
// 销毁所有的Cube对象
private void ClearCubes()
{
int childCount = cubesList.Count;
for (int i = childCount - 1; i >= 0; i--)
{
GameObject obj = cubesList[i].gameObject;
ObjectPool.Instance.ReturnObject(obj);
}
currentCube = null;
}
}
4 エフェクト表示
まず、Unity の初期位置 (0,0,0) に新しい平面を作成し、その Scale の X 値と Z 値を適切に増やしてから、新しい Cube オブジェクトを作成し、Rigidbody コンポーネント (重力効果) を追加します。これをプレハブに追加し、シーン内に空のオブジェクトを作成し、NewCube スクリプトをマウントして、完成した Cube プレハブをその CubePrefab パラメーターにドラッグします。
ビデオデモ:
5 Unity Asset Store 用のオブジェクト プール プラグイン
Unity リソース ストアのパッケージ化されたオブジェクト プール プラグイン: Lean Pool は、Unity の組み込みオブジェクト プール API を
直接使用し、次の名前空間を使用してインポートすることもできます。
using UnityEngine.Pool;