【Unity】ObjectPoolをちょっとだけラクに実装する
はじめに
Unityではオブジェクトの生成・破棄は負荷が大きいため、大量に呼び出すとフレームレートの低下(最悪の場合は処理落ち)してしまいます。
そこでオブジェクトプールというオブジェクトの生成に関するパターンがあります。これはオブジェクトの数を制限し、再利用したい時に利用するパターンです。
少し制限がありますが、その分ちょっとラク?に実装する方法を紹介します。
実装する
ObjectPoolクラス
抽象クラスとして扱います。
using System.Collections.Generic; using UnityEngine; /// <summary> /// ObjectPool /// </summary> /// <typeparam name="T">Component限定</typeparam> public abstract class ObjectPool<T> : MonoBehaviour where T : Component { /// <summary> /// Poolしたいオブジェクト /// </summary> [SerializeField] private T poolObject; /// <summary> /// PoolのためのList /// </summary> private List<T> _poolList; /// <summary> /// Poolするオブジェクトの親オブジェクト /// </summary> [SerializeField] private Transform poolParent; /// <summary> /// 初期化時にPoolする数 /// </summary> [SerializeField] private int initializePoolCount; protected virtual void Awake() { _poolList = new List<T>(); for (int i = 0; i < initializePoolCount; i++) { var pool = CreatePool(Vector3.zero, Quaternion.identity); pool.gameObject.SetActive(false); } } /// <summary> /// Poolを新しく生成 /// </summary> /// <param name="createPosition"></param> /// <param name="createRotation"></param> /// <returns></returns> private T CreatePool(Vector3 createPosition, Quaternion createRotation) { var newPoolObject = Instantiate(poolObject, createPosition, createRotation); newPoolObject.transform.parent = poolParent; _poolList.Add(newPoolObject); return newPoolObject; } /// <summary> /// Poolしたオブジェクトから再利用 or 新しく生成 /// </summary> /// <param name="setPosition"></param> /// <param name="setRotation"></param> /// <returns>Poolしたオブジェクト</returns> protected T ActivatePool(Vector3 setPosition, Quaternion setRotation) { foreach (var pool in _poolList) { if (pool == null) { continue; } if (pool.gameObject.activeSelf) { continue; } pool.transform.position = setPosition; pool.transform.rotation = setRotation; pool.gameObject.SetActive(true); return pool; } return CreatePool(setPosition, setRotation); } /// <summary> /// Poolした全オブジェクトの破棄 /// </summary> protected void ClearAllPool() { foreach (var pool in _poolList) { Destroy(pool.gameObject); } _poolList.Clear(); } }
ObjectPoolを扱うクラス
Playerなど、Poolするオブジェクトを生成するclassです。
ObjectPool<PoolしたいオブジェクトのComponent>
を継承する必要があります。
Poolする対象をComponentに限定しているため、GameObject型
などは扱えません。
今回はTestBulletをPoolする対象とします。
using UnityEngine; public class TestPlayer : ObjectPool<TestBullet> { private Transform _generateTransform; protected override void Awake() { // Awakeで何か宣言したい場合↓を呼ぶ必要アリ // base.Awake(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { // Poolしたオブジェクトの利用 var pos = _generateTransform.position; var rot = _generateTransform.rotation; ActivatePool(pos, rot); } } }
Poolする対象
利用時の初めに必要な処理がある場合はOnEnable()
を呼びます。
また、破棄したいタイミングではDestroy()
の代わりにSetActive(false)
を呼びます。
using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class TestBullet : MonoBehaviour { private Rigidbody _rigidbody; private void Awake() { _rigidbody = GetComponent<Rigidbody>(); } private void OnEnable() { _rigidbody.velocity = transform.forward; } private void OnTriggerEnter(Collider other) { gameObject.SetActive(false); } }
さいごに
説明不足な部分が多々あると思いますが、ご容赦ください。
また、間違いやアドバイス等ありましたらドシドシお願いします!!!
UniRxにあるObjectPoolの方が便利です(早く覚えないと...!)