Unity3Dのシンプルオブジェクトプール

開発プロセス中に、オブジェクトのプールを作成する必要に直面しました。 この記事や他の記事を読んだ後、オブジェクトを行(プレハブ名)でアクセスできるように、ニーズに合わせて簡単なプールを作成することにしました。



それでは始めましょう。 プールは4つのスクリプトで構成されています。 プール内のオブジェクトのオン/オフ状態は、追加の変数が目立たないように、Unity activeInHierarchyプロパティによって決定されます。



1.プールオブジェクト



プールオブジェクトコンポーネントは、プールで使用されるオブジェクト上に存在する必要があります。 その主な目的は、オブジェクトをプールに戻すことです。



using UnityEngine; using System.Collections; using System.Collections.Generic; [AddComponentMenu("Pool/PoolObject")] public class PoolObject : MonoBehaviour { #region Interface public void ReturnToPool () { gameObject.SetActive (false); } #endregion }
      
      





クラスには1つのメソッドがあります。 実際、これなしでも実行できますが、この方法では、(通常の方法で破棄された)プールではなく、プールを目的としたオブジェクトを分離します。



2.オブジェクトプーリング



どうぞ オブジェクトプーリングクラスはプール自体であり、必要に応じて無料のオブジェクトを発行し、不足すると新しいオブジェクトを作成します。



 using UnityEngine; using System.Collections; using System.Collections.Generic; [AddComponentMenu("Pool/ObjectPooling")] public class ObjectPooling { #region Data List<PoolObject> objects; Transform objectsParent; #endregion
      
      





ここで、オブジェクトはプールに含まれるすべてのオブジェクトであり、objectsParentはステージ上の階層の親としてのみ使用されます(したがって、オブジェクトのシートはありません)。



追加は、AddObjectメソッドを使用して行われます。このメソッドは、追加するサンプルとステージ上の階層の親を取得します。



 void AddObject(PoolObject sample, Transform objects_parent) { GameObject temp = GameObject.Instantiate(sample.gameObject); temp.name = sample.name; temp.transform.SetParent (objects_parent); objects.Add(temp.GetComponent<PoolObject> ()); if (temp.GetComponent<Animator> ()) temp.GetComponent<Animator> ().StartPlayback (); temp.SetActive(false); }
      
      





Gameobject tempが作成され、サンプルの名前が割り当てられた後、リストに追加されます。 その後、オブジェクトは外部から「要求」されるまでオフになります。



行については別に:



  if (temp.GetComponent<Animator> ()) temp.GetComponent<Animator> ().StartPlayback ();
      
      





導入された理由は それらを使用せずにオブジェクトを作成すると、アニメーターが起動しませんでした(更新プログラムを呼び出すことができませんでした)。 その結果、たとえばシーンの開始時に100個の弾丸が作成され、すぐにオフになり、50個の弾丸が要求されたときに、アニメーターが同時に開始し、FPSがサグしました(多くのオブジェクトでアニメーションが常に再生されています)。 プロジェクトがアニメータで多数のオブジェクトを使用する予定がない場合、このコードは必要ありません。



初期化を検討してください:



  public void Initialize (int count, PoolObject sample, Transform objects_parent) { objects = new List<PoolObject> (); // List objectsParent = objects_parent; //      for (int i=0; i<count; i++) { AddObject(sample, objects_parent); //     } }
      
      





このクラスの2番目のメソッドはGetObject()で、Gameobjectを返します。



  public PoolObject GetObject () { for (int i=0; i<objects.Count; i++) { if (objects[i].gameObject.activeInHierarchy==false) { return objects[i]; } } AddObject(objects[0], objectsParent); return objects[objects.Count-1]; }
      
      





ロジックは単純です-プール内のオブジェクトのいずれかがオフ(つまり、フリー)である場合、シートを調べます-それを返すか、そうでない場合は新しいオブジェクトを追加します。



3. PoolManager



次のPoolManagerクラスは、さまざまなオブジェクトのプールを管理します。 クラスは、オブジェクトへのアクセスを簡素化するために静的です。 シングルトーン、インスタンスなどを作成する必要はありません。



 using UnityEngine; using System.Collections; using System.Collections.Generic; public static class PoolManager{ private static PoolPart[] pools; private static GameObject objectsParent; [System.Serializable] public struct PoolPart { public string name; //  public PoolObject prefab; // ,   public int count; //     public ObjectPooling ferula; //  }
      
      





すべての情報はPoolPart構造に保存されます。



初期化は、これらの構造体の配列によって実行されます(フェルラ、おそらくあまり良い名前ではありませんが、プールのヒープで混乱しないようにします):



  public static void Initialize(PoolPart[] newPools) { pools = newPools; //  objectsParent = new GameObject (); objectsParent.name = "Pool"; //    Pool,     for (int i=0; i<pools.Length; i++) { if(pools[i].prefab!=null) { pools[i].ferula = new ObjectPooling(); //      pools[i].ferula.Initialize(pools[i].count, pools[i].prefab, objectsParent.transform); //     } } }
      
      





この静的クラスの2番目のメソッドはGetObjectです。これは、標準のInstantiateに類似していますが、オブジェクトの名前によって異なります。 既存のすべてのプールをチェックし、適切なプールが見つかった場合、ObjectPoolingクラスからGetObject()メソッドを取得します。



  public static GameObject GetObject (string name, Vector3 position, Quaternion rotation) { GameObject result = null; if (pools != null) { for (int i = 0; i < pools.Length; i++) { if (string.Compare (pools [i].name, name) == 0) { //       result = pools[i].ferula.GetObject ().gameObject; //    result.transform.position = position; result.transform.rotation = rotation; result.SetActive (true); //    return result; } } } return result; //     ,  null }
      
      





4. PoolSetup



ただし、Unityインスペクターで、プールで使用するオブジェクトとその番号を編集する必要があります。 これを行うには、MonoBehaviourの継承者であるラッパークラスを記述して、オブジェクトにぶら下げる必要があります。



 using UnityEngine; using System.Collections; [AddComponentMenu("Pool/PoolSetup")] public class PoolSetup : MonoBehaviour {//     PoolManager #region Unity scene settings [SerializeField] private PoolManager.PoolPart[] pools; //,            #endregion #region Methods void OnValidate() { for (int i = 0; i < pools.Length; i++) { pools[i].name = pools[i].prefab.name; //  ,   } } void Awake() { Initialize (); } void Initialize () { PoolManager.Initialize(pools); //   } #endregion }
      
      





このクラスはステージ上にある必要があります。そうでない場合、一方が他方のプールを上書きします。



使用する

これで、プールから次のようにオブジェクトを呼び出すことができます。



 Gameobject bullet = PoolManager.GetObject (bulletPrefab.name, shotPoint.position, myTransform.rotation);
      
      





戻ります:



 GetComponent<PoolObject>().ReturnToPool ();
      
      





その結果、プールは機能し、使いやすくなります。 いくつかのスクリーンショット:



エディターでの管理:







弾丸と船のスポーン:







あとがき



もちろん、この実装には多くの欠点があります。 主なものをリストします。



1)文字列アクセスは、たとえば整数識別子キーによるアクセスに置き換えることができます。これにより、作業が高速化されます。

2)エラーおよび例外処理はありません(メソッドは単にnullを返します)。実際にはチェックはありません。

3)ステージ上でのPoolSetupシングルトンの必要性。ただし、誰も言及していません。



更新: GitHubリポジトリ



完全なコード



プールオブジェクト
 using UnityEngine; using System.Collections; using System.Collections.Generic; [AddComponentMenu("Pool/PoolObject")] public class PoolObject : MonoBehaviour { #region Interface public void ReturnToPool () { gameObject.SetActive (false); } #endregion }
      
      







オブジェクトプーリング


 using UnityEngine; using System.Collections; using System.Collections.Generic; [AddComponentMenu("Pool/ObjectPooling")] public class ObjectPooling { #region Data List<PoolObject> objects; Transform objectsParent; #endregion #region Interface public void Initialize (int count, PoolObject sample, Transform objects_parent) { objects = new List<PoolObject> (); objectsParent = objects_parent; for (int i=0; i<count; i++) { AddObject(sample, objects_parent); } } public PoolObject GetObject () { for (int i=0; i<objects.Count; i++) { if (objects[i].gameObject.activeInHierarchy==false) { return objects[i]; } } AddObject(objects[0], objectsParent); return objects[objects.Count-1]; } #endregion #region Methods void AddObject(PoolObject sample, Transform objects_parent) { GameObject temp; temp = GameObject.Instantiate(sample.gameObject); temp.name = sample.name; temp.transform.SetParent (objects_parent); objects.Add(temp.GetComponent<PoolObject> ()); if (temp.GetComponent<Animator> ()) temp.GetComponent<Animator> ().StartPlayback (); temp.SetActive(false); } #endregion }
      
      







プールマネージャー
 using UnityEngine; using System.Collections; using System.Collections.Generic; public static class PoolManager{ private static PoolPart[] pools; private static GameObject objectsParent; [System.Serializable] public struct PoolPart { public string name; public PoolObject prefab; public int count; public ObjectPooling ferula; } public static void Initialize(PoolPart[] newPools) { pools = newPools; objectsParent = new GameObject (); objectsParent.name = "Pool"; for (int i=0; i<pools.Length; i++) { if(pools[i].prefab!=null) { pools[i].ferula = new ObjectPooling(); pools[i].ferula.Initialize(pools[i].count, pools[i].prefab, objectsParent.transform); } } } public static GameObject GetObject (string name, Vector3 position, Quaternion rotation) { GameObject result = null; if (pools != null) { for (int i = 0; i < pools.Length; i++) { if (string.Compare (pools [i].name, name) == 0) { result = pools[i].ferula.GetObject ().gameObject; result.transform.position = position; result.transform.rotation = rotation; result.SetActive (true); return result; } } } return result; } }
      
      







Poolsetup
 using UnityEngine; using System.Collections; [AddComponentMenu("Pool/PoolSetup")] public class PoolSetup : MonoBehaviour {//     PoolManager #region Unity scene settings [SerializeField] private PoolManager.PoolPart[] pools; #endregion #region Methods void OnValidate() { for (int i = 0; i < pools.Length; i++) { pools[i].name = pools[i].prefab.name; } } void Awake() { Initialize (); } void Initialize () { PoolManager.Initialize(pools); } #endregion }
      
      








All Articles