Pool объектов для Unity3d

от автора


Всем привет.

Все знают что операции создания и удаления объектов обычно не дешевые. Например создавать каждый раз пулю и уничтожать, довольно накладно для тех же мобильных устройств. Может стоит не уничтожать пулю, а скрывать ее. Вот решил поделится своей реализацией Pool Manager. Который использую в разных проектах, в том числе и на photon server.

Структуры

Для начала, нужно создать интерфейс.

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

Где метод Create() будет играть роль псевдо-конструктора. Ведь когда вы достанете объект из пула, его состояние будет не определено, что может пагубно отразится на дальнейшем его использовании.

Теперь сам Pool Manager

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 — поле максимального количества pool объектов. В случае, если не возможно поместить в пул очередной объект, сам объект вывозит метод FailedPush();
OnPush() — непосредственно перед попаданием в пул.
Create() — перед тем, как пул вернет нам экземпляр.

Общий Pool готов. Теперь нужно сделать вариацию для 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;     } } 

По сути это просто обвертка над первым пулом и Сингелтон.
PopOrCreate() — нам понадобится вот этот метод для создания объектов.
Push() — у самих пул объектов или Push в менеджере.

Теперь нам понадобится сам 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);     } }  

Все объекты будем наследовать от него.
FailedPush() — стоит обратить внимание на этот метод. Возможно вы захотите бросать исключение, мол пул забит или что-то еще.

Теперь перейдем к использованию. На примере пули следов и UI List item.

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);  // ---- 

Надеюсь данный пример поможет вам в написании своих проектов на Unity3d/

ссылка на оригинал статьи http://habrahabr.ru/post/255499/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *