Pool objects for Unity3d

Everyone knows that the operations of creating and deleting objects are costly. For example to create an event each time a bullet and destroyed it , is rather expensive for mobile devices. Maybe we should not destroy the bullet but hide it. That's decided to share his realization Pool Manager. Which are used in various projects, including the photon server.

Structure

First, you need to create an interface:

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

Where the method Create () will play the role of pseudo-constructor. After all, when you pull the object out of the pool, his condition is not specified, which may be detrimental to its further use.

Now 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);
            }
        }
    }
}

Here is what  you should pay attention to.
MaxInstances - field the maximum number of pool facilities. If not possible to place the next object in the pool, the method removes the object FailedPush ();
OnPush () - just before entering the pool.
Create () - before the pool will return us a copy.

Shared Pool is ready. Now you need to make a variation for Unity3d. proceed

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;
    }
}

In fact it's just a wrapper over the first pool and Singleton.
PopOrCreate () - here we need this method to create objects.
Push () - they themselves object pooling or Push manager.

Now we need a very 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);
    }
}

All objects will inherit from it.
Failed Push () - pay attention to this method. You may want to throw an exception, they say the pool is blocked or something else.

Now let's move to use. On the example of the bullet marks and UI List item.

public class Bullet : UnityPoolObject 
{
...
}
// create
 Bullet bullet = UnityPoolManager.Instance.PopOrCreate<Bullet>(bulletPrefab, bulletPoint.position, Quaternion.identity);
  bullet.Execute(sender, bulletPoint.position, CalculateTarget(target, accuracy01), damage, blockTime, range, bulletSpeed);
// destruction, or rather returned to the pool
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);

// ----

I hope this example will help you in writing your projects Unity3d.

 

All data posted on the site represents accessible information that can be browsed and downloaded for free from the web.

http://habrahabr.ru/post/255499/

 

User replies

No replies yet