Unity3dのプールオブジェクト

オブジェクトの作成と削除が安くないことは誰もが知っています。 たとえば、同じモバイルデバイスの場合、毎回弾丸を作成して破壊するのは非常に高価です。 たぶん、弾丸を破壊するのではなく、隠す必要があります。 そこで、Pool Managerの実装を共有することにしました。



構造



開始するには、インターフェースを作成する必要があります。

public interface IPoolObject<T> { T Group { get; } void Create(); void OnPush(); void FailedPush(); }
      
      





インターフェースは正確に何のためですか? 普遍的に、プールが動作するオブジェクトを知らなくても、プールが特別なメソッドとプロパティを持っていることだけを知っています。

T-この場合、グループ識別子。 さらに例。

Create()メソッド-疑似コンストラクターの役割を果たします。 結局、プールからオブジェクトを取得するとき、その状態は決定されず、それがその後の使用に悪影響を与える可能性があります。

OnPush()メソッド-擬似デストラクタの役割を果たします。 オブジェクトがプールに落ちた場合、おそらく何かをする必要があります。たとえば、接続されているパーティションや他の何かをオフにします。

FailedPush()メソッド-プールに入ることができない場合、オブジェクトで呼び出されます。 たとえば、プールがいっぱいです。 そして、何かがオブジェクトを所有していなかったなら、それは破壊を必要とするかもしれません。



今プールマネージャー自体

 using System.Collections.Generic; using System; public class PoolManager<K, V> where V :IPoolObject<K> { public virtual int MaxInstances { get; protected set; } public virtual int InctanceCount { get { return objects.Count; } } public virtual int CacheCount { get { return cache.Count; } } public delegate bool Compare<T>(T value) where T: V; protected Dictionary<K, List<V>> objects; protected Dictionary<Type, List<V>> cache; public PoolManager(int maxInstance) { MaxInstances = maxInstance; objects = new Dictionary<K, List<V>>(); cache = new Dictionary<Type, List<V>>(); } public virtual bool CanPush() { return InctanceCount + 1 < MaxInstances; } public virtual bool Push(K groupKey, V value) { bool result = false; if (CanPush()) { value.OnPush(); if (!objects.ContainsKey(groupKey)) { objects.Add(groupKey, new List<V>()); } objects[groupKey].Add(value); Type type = value.GetType(); if (!cache.ContainsKey(type)) { cache.Add(type, new List<V>()); } cache[type].Add(value); } else { value.FailedPush(); } return result; } public virtual T Pop<T>(K groupKey) where T : V { T result = default(T); if (Contains(groupKey) && objects[groupKey].Count > 0) { for (int i = 0; i < objects[groupKey].Count; i++) { if (objects[groupKey][i] is T) { result = (T)objects[groupKey][i]; Type type = result.GetType(); RemoveObject(groupKey, i); RemoveFromCache(result, type); result.Create(); break; } } } return result; } public virtual T Pop<T>() where T: V { T result = default(T); Type type = typeof(T); if (ValidateForPop(type)) { for (int i = 0; i < cache[type].Count; i++) { result = (T)cache[type][i]; if (result != null && objects.ContainsKey(result.Group)) { objects[result.Group].Remove(result); RemoveFromCache(result, type); result.Create(); break; } } } return result; } public virtual T Pop<T>(Compare<T> comparer) where T : V { T result = default(T); Type type = typeof(T); if (ValidateForPop(type)) { for(int i = 0; i < cache[type].Count; i++) { T value = (T)cache[type][i]; if (comparer(value)) { objects[value.Group].Remove(value); RemoveFromCache(result, type); result = value; result.Create(); break; } } } return result; } public virtual bool Contains(K groupKey) { return objects.ContainsKey(groupKey); } public virtual void Clear() { objects.Clear(); } protected virtual bool ValidateForPop(Type type) { return cache.ContainsKey(type) && cache[type].Count > 0; } protected virtual void RemoveObject(K groupKey, int idx) { if (idx >= 0 && idx < objects[groupKey].Count) { objects[groupKey].RemoveAt(idx); if (objects[groupKey].Count == 0) { objects.Remove(groupKey); } } } protected void RemoveFromCache(V value, Type type) { if (cache.ContainsKey(type)) { cache[type].Remove(value); if (cache[type].Count == 0) { cache.Remove(type); } } } }
      
      





注目すべきこと。

MaxInstances-プールオブジェクトの最大数のフィールド。 別のオブジェクトをプールに配置できない場合。

保護された辞書<K、リスト>オブジェクト-コンテナーはグループリストとして表示されます。 プールに入ると、適切なグループに送信されます。 必要な場合、プールはグループ内の最初の対応するオブジェクトを返します。 保護された辞書<タイプ、リスト>キャッシュ-タイプごとのオブジェクトキャッシュ。 美しいPop()メソッド専用に必要です。

メソッドPop(比較演算子の比較)-条件によってオブジェクトを取得する必要がある場合があります。



共有プールの準備ができました。 次に、Unity3dのバリエーションを作成する必要があります。 さあ始めましょう



 using UnityEngine; using System.Collections; public class UnityPoolManager : MonoBehaviour { public static UnityPoolManager Instance {get; protected set;} public int maxInstanceCount = 128; protected PoolManager<string, UnityPoolObject> poolManager; protected virtual void Awake() { Instance = this; poolManager = new PoolManager<string, UnityPoolObject>(maxInstanceCount); } public virtual bool CanPush() { return poolManager.CanPush(); } public virtual bool Push(string groupKey, UnityPoolObject poolObject) { return poolManager.Push(groupKey, poolObject); } public virtual T PopOrCreate<T>(T prefab) where T : UnityPoolObject { return PopOrCreate(prefab, Vector3.zero, Quaternion.identity); } public virtual T PopOrCreate<T>(T prefab, Vector3 position, Quaternion rotation) where T : UnityPoolObject { T result = poolManager.Pop<T>(prefab.Group); if (result == null) { result = CreateObject<T>(prefab, position, rotation); } else { result.SetTransform(position, rotation); } return result; } public virtual UnityPoolObject Pop(string groupKey) { return poolManager.Pop<UnityPoolObject>(groupKey); } public virtual T Pop<T>() where T : UnityPoolObject { return poolManager.Pop<T>(); } public virtual T Pop<T>(PoolManager<string, UnityPoolObject>.Compare<T> comparer) where T : UnityPoolObject { return poolManager.Pop<T>(comparer); } public virtual T Pop<T>(string groupKey) where T : UnityPoolObject { return poolManager.Pop<T>(groupKey); } public virtual bool Contains(string groupKey) { return poolManager.Contains(groupKey); } public virtual void Clear() { poolManager.Clear(); } protected virtual T CreateObject<T>(T prefab, Vector3 position, Quaternion rotation) where T : UnityPoolObject { GameObject go = Instantiate(prefab.gameObject, position, rotation) as GameObject; T result = go.GetComponent<T>(); result.name = prefab.name; return result; } }
      
      





基本的に、それは最初のプールとSingeltonの単なるラッパーです。 別の方法でラップすることもできます。 しかし、UnityPoolManager.Instance.Pool.Pop()のようなものになり、さらにユニット専用のメソッドをいくつか作成するだけです。 しかし、これは読者の裁量です。 コードは少なくなりますが、追加のプールが表示されます。



PopOrCreate()-オブジェクトを作成するにはこのメソッドが必要です。

プッシュ()-オブジェクト自体のプールで、またはマネージャーでプッシュ。



今、GameObject自体が必要です



 using UnityEngine; using System.Collections; public class UnityPoolObject : MonoBehaviour, IPoolObject<string> { public virtual string Group { get {return name;} } //    public Transform MyTransform { get { return myTransform; } } protected Transform myTransform; protected virtual void Awake() { myTransform = transform; } public virtual void SetTransform(Vector3 position, Quaternion rotation) { myTransform.position = position; myTransform.rotation = rotation; } public virtual void Create() //    { gameObject.SetActive(true); } public virtual void OnPush() //    { gameObject.SetActive(false); } public virtual void Push() //   { UnityPoolManager.Instance.Push(Group, this); } public void FailedPush() //      { Debug.Log("FailedPush"); // !!! Destroy(gameObject); } }
      
      





それからすべてのオブジェクトを継承し、CreateメソッドとOnPushメソッドを再定義できます。



それでは、使用に移りましょう。 たとえば、箇条書きとUIリスト項目。



 public class Bullet : UnityPoolObject //   ,    { ... } //  Bullet bullet = UnityPoolManager.Instance.PopOrCreate<Bullet>(bulletPrefab, bulletPoint.position, Quaternion.identity); bullet.Execute(sender, bulletPoint.position, CalculateTarget(target, accuracy01), damage, blockTime, range, bulletSpeed); ... // ,     //   -    timer-= Time.deltaTime; if (timer< 0) { Push(); }
      
      







 public class StepObject : UnityPoolObject //  { ... } ///         StepObject stepObject = UnityPoolManager.Instance.PopOrCreate<StepObject>(prefab, sender.position, sender.rotation); FXManager.Instance.InitDecal(null, stepObject.gameObject, hit, direction); stepObject.MyTransform.rotation *= rotation; StartCoroutine(ApplyDecalC(stepObject)); ///   protected virtual IEnumerator ApplyDecalC(StepObject decalObject) { yield return new WaitForSeconds(waitTime); //  -  yield return StartCoroutine(FXManager.Instance.HideOjectC(decalObject.gameObject, hideTime)); //    decalObject.Push(); //   ,    }
      
      







 public class ProfileListItem : UnityPoolObject { ... } //   -  ProfileListItem profileItem = UnityPoolManager.Instance.PopOrCreate(prefab); ... //   profileItem.profileId = profileId; list.AddItem(profileItem); //    //  ,   .      Push foreach(ProfileListItem item in list) { item.Push(); }
      
      







この例が、Unity3dでプロジェクトを作成するのに役立つことを願っています。 インターフェースだけでなく、より詳細に説明する必要があると提案してくれた@ lexxpavlovに特に感謝します。



All Articles