Unity Community

Register or Sign In:

+ Reply to Thread
Results 1 to 8 of 8

  1. Posts
    129

    All-Purpose Object Pooling System: Criticisms, Suggestions?

    I've written a generic object pooling class that should be able to pool instances of any class that inherits from UnityEngine.Object without making any changes to that class. I've also written some classes that simplify pooling for commonly used object types like GameObjects and MonoBehaviours, and an interface so that they can be used interchangeably after construction and new classes can be written easily. I'd like to put it on the Unify Community Wiki, but first I want to make sure that the code is as useful, concise and intuitive as it can be. Suggestions welcome.

    sample use of GameObjectPool:
    Code:  
    1. IPool<GameObject> goPool =  new GameObjectPool(prefab, 40);
    2. GameObject poolie = goPool.Get();
    3. goPool.Return(poolie);

    sample use of GenericPool with anonymous methods:
    Code:  
    1. IPool<CustomType> customPool = new GenericPool<CustomType>
    2.     (
    3.         delegate() {return new CustomType();},
    4.         80,
    5.         delegate(CustomType customType) {customType.value = 0;},
    6.         delegate(CustomType customType) {customType.activated = false;},
    7.         delegate(CustomType customType) {customType.activated = true;},
    8.     );

    sample use of GenericPool with an object to copy and method references:
    Code:  
    1. IPool<CustomType> customPool = new GenericPool<CustomType> (customInstance, 80, Reset, Disable, Enable);

    the code itself:
    Code:  
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System;
    5. using System.Linq;
    6.  
    7. public interface IPool<T> {
    8.     T Get();
    9.     void Return (T objectToPool);
    10.     void CopyTo (T[] array);
    11.    
    12.     int Count{get;}
    13. }
    14.  
    15. public class GenericPool<T> : UnityEngine.Object, IPool<T> where T : UnityEngine.Object {
    16.     protected HashSet<T> pool;
    17.    
    18.     protected T original; //if provided, all pool objects will be copies of this object
    19.     protected Func<T> Constructor; //creates the pool objects from a given method.
    20.     protected Action<T> Recycler; //called on each object when it is returned to the pool. it should reset all changed values.
    21.    
    22.     protected Action<T> Disabler;
    23.     //called on each object when it is returned to the pool. it should "hide" the object from anything that might try to
    24.     //interact with it, or keep the object from doing anything.
    25.    
    26.     protected Action<T> Activator;
    27.     //called on each object when it is gotten from the pool. it should undo whatever the disabler did so that the object
    28.     //is able to act freely again.
    29.    
    30.     public int Count {get { return pool.Count; }} //gets the current total number of objects in pool.
    31.    
    32.     //create a pool containing copies of a given object.
    33.     public GenericPool (T original, int initialSize, Action<T> Recycler, Action<T> Disabler, Action<T> Activator) {
    34.         this.original = original;
    35.         this.Recycler = Recycler;
    36.         this.Disabler = Disabler;
    37.         this.Activator = Activator;
    38.        
    39.         for (int i = 0; i < initialSize; i++)
    40.         {
    41.             pool.Add(MakeNew());
    42.         }
    43.     }
    44.    
    45.     //create a pool of objects built by a passed-in function.
    46.     public GenericPool (Func<T> Constructor, int initialSize, Action<T> Recycler, Action<T> Disabler, Action<T> Activator) {
    47.         this.Constructor = Constructor;
    48.         this.Recycler = Recycler;
    49.         this.Disabler = Disabler;
    50.         this.Activator = Activator;
    51.        
    52.         for (int i = 0; i < initialSize; i++)
    53.         {
    54.             pool.Add(MakeNew());
    55.         }
    56.     }
    57.    
    58.     protected GenericPool () {
    59.        
    60.     }
    61.    
    62.     //creates a new instance of whatever object we're pooling
    63.     protected virtual T MakeNew () {
    64.         T copy;
    65.         if (original != null) //if the pool was provided an original objec to copy, copy it
    66.         {
    67.             copy = Instantiate(original) as T;
    68.             Disabler(copy);
    69.         }
    70.         else if (Constructor != null) //if a constructor was supplied instead, call it
    71.         {
    72.             copy = Constructor();
    73.             Disabler(copy);
    74.         }
    75.         else
    76.         {
    77.             copy = null;
    78.             Debug.Log("couldn't create object for pool because there was no original to copy and no constructor provided.");
    79.         }
    80.         return copy;
    81.     }
    82.    
    83.     T IPool<T>.Get () {
    84.         T unPooledObject;
    85.        
    86.         if (Count == 0)
    87.         {
    88.             unPooledObject = MakeNew();
    89.         }
    90.         else
    91.         {
    92.             unPooledObject = pool.First();
    93.             pool.Remove(unPooledObject);
    94.         }
    95.         if (Activator != null)
    96.         {
    97.             Activator(unPooledObject);
    98.         }
    99.         return unPooledObject;
    100.     }
    101.    
    102.     void IPool<T>.Return (T objectToPool) {
    103.         if (Recycler != null)
    104.         {
    105.             Recycler(objectToPool);
    106.         }
    107.         if (Disabler != null)
    108.         {
    109.             Disabler(objectToPool);
    110.         }
    111.         pool.Add(objectToPool);
    112.     }
    113.    
    114.     void IPool<T>.CopyTo (T[] array) {
    115.         pool.CopyTo(array);
    116.     }
    117. }
    118.  
    119. public class GameObjectPool : GenericPool<GameObject> {
    120.  
    121.     public GameObjectPool (GameObject original, int initialSize, Action<GameObject> Recycler = null) {
    122.         pool = new HashSet<GameObject>();
    123.         this.original = original;
    124.         this.Recycler = Recycler;
    125.         this.Disabler = Disable;
    126.         this.Activator = Activate;
    127.        
    128.         for (int i = 0; i < initialSize; i++)
    129.         {
    130.             pool.Add(MakeNew());
    131.         }
    132.     }
    133.    
    134.     private void Disable (GameObject go) {
    135.         go.SetActiveRecursively(false);
    136.         go.hideFlags = HideFlags.HideInHierarchy;
    137.     }
    138.    
    139.     private void Activate (GameObject go) {
    140.         go.hideFlags = 0;
    141.         go.SetActiveRecursively(true);
    142.     }
    143. }
    144.  
    145. public class ComponentPool<T> : GenericPool<T> where T : UnityEngine.Component {
    146.     protected GameObject attatchedObject;
    147.    
    148.     public ComponentPool (GameObject attatchedObject, int initialSize, Func<T> Constructor = null, Action<T> Recycler = null) {
    149.         pool = new HashSet<T>();
    150.         this.attatchedObject = attatchedObject;
    151.         this.Constructor = Constructor;
    152.         this.Recycler = Recycler;
    153.        
    154.         for (int i = 0; i < initialSize; i++)
    155.         {
    156.             pool.Add(MakeNew());
    157.         }
    158.     }
    159.    
    160.     protected ComponentPool () {
    161.        
    162.     }
    163.    
    164.     protected override T MakeNew () {
    165.         T copy;
    166.        
    167.         if (Constructor != null)
    168.         {
    169.             copy = Constructor();
    170.         }
    171.         else
    172.         {
    173.             copy = attatchedObject.AddComponent<T>();
    174.         }
    175.         return copy;
    176.     }
    177. }
    178.  
    179. public class MonoBehaviourPool<T> : ComponentPool<T> where T : UnityEngine.MonoBehaviour {
    180.    
    181.     public MonoBehaviourPool (GameObject attatchedObject, int initialSize, Func<T> Constructor = null, Action<T> Recycler = null) {
    182.         pool = new HashSet<T>();
    183.         this.attatchedObject = attatchedObject;
    184.         this.Constructor = Constructor;
    185.         this.Recycler = Recycler;
    186.         this.Disabler = Disable;
    187.         this.Activator = Activate;
    188.        
    189.         for (int i = 0; i < initialSize; i++)
    190.         {
    191.             T copy = MakeNew();
    192.             copy.enabled = false;
    193.             pool.Add(copy);
    194.         }
    195.     }
    196.    
    197.     private void Activate (T behaviour) {
    198.         behaviour.enabled = true;
    199.     }
    200.    
    201.     private void Disable (T behaviour) {
    202.         behaviour.CancelInvoke();
    203.         behaviour.StopAllCoroutines();
    204.         behaviour.enabled = false;
    205.     }
    206. }
    Last edited by teatime; 03-25-2011 at 05:35 PM.


  2. Location
    Argentina
    Posts
    378
    Teatime, thanks for your work.
    Maybe a singletone with a hastable, list, or an array, could make a similar work.
    In may case, i use what i say... plus a couple of method to register and unregister the object which can be, virtually, whatever (unless you use GENERIC... which is a cool feature but for other scope).
    We will see what the people says!

    Thanks again.
    I'm a spanish native speaker, so excuse me if i make a mistake.

    You don't know nothing about scripting? Take a look at this [link]


  3. Posts
    129
    i thought about making it inherently a singleton, but that seemed too inflexible. creating a static dictionary<string, IPool> in another class should provide the same easy-access functionality, and this allows for strictly local pools as well.
    Last edited by teatime; 03-16-2011 at 01:31 PM.


  4. Location
    Amsterdam
    Posts
    130
    Can you post a simple demo scene with for example pooling some cubes and spheres?


    Thanks,


    Peter.


  5. Location
    Amsterdam
    Posts
    130
    I can't figure out how to create a global objectpool. In my case it will hold bullets that can be fetched from all other scripts. If I make IPool<GameObject> goPool = new GameObjectPool(prefab, 40); public I will get the following error:

    PoolObjects.cs(10,34): error CS0052: Inconsistent accessibility: field type `IPool<UnityEngine.GameObject>' is less accessible than field `PoolObjects.goPool'

    Please help ;-)


    Peter.
    Last edited by TriplePAF; 03-25-2011 at 04:48 PM.


  6. Posts
    129
    wow, sorry, just add the word "public" to the beginning of the line "interface IPool<T> {" or c+p the edited version in the first post and that error should go away. somehow it was there on my copy but not in here.


  7. Location
    Amsterdam
    Posts
    130
    That fixed the problem ;-). I implemented already a few pools and the game starts to run smoothly.

    What happens if I don't return objects back to the pool and just destroy them? Does the pool generate on the fly new copies to get the maximum precache again? I have some objects that are somewhat difficult to recycle like particle effects.


    Peter.


  8. Posts
    129
    if you get an object from the pool when it is empty, it will automatically generate and send you a new object. the pool keeps no reference of currently unpooled objects so it makes no difference to the pool if you just destroy them.