[recovery mode] Pool объектов для Unity3d

Все знают что операции создания и удаления объектов не дешевые. Например создавать каждый раз пулю и уничтожать, довольно накладно для тех же мобильных устройств. Может стоит не уничтожать пулю, а скрывать ее. Вот решил поделится своей реализацией Pool Manager. Который использую в разных проектах, в том числе и на photon server.СтруктурыДля начала, нужно создать интерфейс: public interface IPoolObject { T Group { get; } void Create (); void OnPush (); void FailedPush (); } Где метод Create () будет играть роль псевдо-конструктора. Ведь когда вы достанете объект из пула, его состояние будет не определено, что может пагубно отразится на дальнейшем его использовании.Теперь сам Pool Manager

using System.Collections.Generic; using System;

public class PoolManager where V: IPoolObject { 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 value) where T: V;

protected Dictionary> objects; protected Dictionary> cache;

public PoolManager (int maxInstance) { MaxInstances = maxInstance; objects = new Dictionary>(); cache = new Dictionary>(); }

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()); } objects[groupKey].Add (value); Type type = value.GetType (); if (! cache.ContainsKey (type)) { cache.Add (type, new List()); } cache[type].Add (value); } else { value.FailedPush (); } return result; }

public virtual T Pop(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() 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(Compare 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 poolManager;

protected virtual void Awake () { Instance = this; poolManager = new PoolManager(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 prefab) where T: UnityPoolObject { return PopOrCreate (prefab, Vector3.zero, Quaternion.identity); }

public virtual T PopOrCreate(T prefab, Vector3 position, Quaternion rotation) where T: UnityPoolObject { T result = poolManager.Pop(prefab.Group); if (result == null) { result = CreateObject(prefab, position, rotation); } else { result.SetTransform (position, rotation); } return result; }

public virtual UnityPoolObject Pop (string groupKey) { return poolManager.Pop(groupKey); }

public virtual T Pop() where T: UnityPoolObject { return poolManager.Pop(); }

public virtual T Pop(PoolManager.Compare comparer) where T: UnityPoolObject { return poolManager.Pop(comparer); }

public virtual T Pop(string groupKey) where T: UnityPoolObject { return poolManager.Pop(groupKey); }

public virtual bool Contains (string groupKey) { return poolManager.Contains (groupKey); }

public virtual void Clear () { poolManager.Clear (); }

protected virtual T CreateObject(T prefab, Vector3 position, Quaternion rotation) where T: UnityPoolObject { GameObject go = Instantiate (prefab.gameObject, position, rotation) as GameObject; T result = go.GetComponent(); result.name = prefab.name; return result; } } По сути это просто обвертка над первым пулом и Сингелтон.PopOrCreate () — нам понадобится вот этот метод для создания объектов.Push () — у самих пул объектов или Push в менеджере.Теперь нам понадобится сам GameObject

using UnityEngine; using System.Collections;

public class UnityPoolObject: MonoBehaviour, IPoolObject { 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(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(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.

© Habrahabr.ru