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 } 

Source: https://habr.com/ru/post/J275091/


All Articles