Всем привет.
Все знают что операции создания и удаления объектов обычно не дешевые. Например создавать каждый раз пулю и уничтожать, довольно накладно для тех же мобильных устройств. Может стоит не уничтожать пулю, а скрывать ее. Вот решил поделится своей реализацией 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/
Добавить комментарий