それでは始めましょう。 プールは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 }