Search Unity

Simple Reusable Object Pool - Help limit your instantiations!

Discussion in 'Assets and Asset Store' started by invulse, Feb 6, 2011.

  1. invulse

    invulse

    Joined:
    Nov 29, 2010
    Posts:
    128
    The script is really simple but it has helped the performance in my iOS game tremendously by limiting the number of instantiations that happen at runtime.

    What it does:

    The object pool allow you to buffer or pool objects you plan on reusing many time throughout your game, by allowing you to request a new object from it, and tell it whether or not it must give you an object or only give you an object thats available. It also buffers objects at the start to give you a pool to work with initially.

    How to use:

    1. Add the ObjectPool.cs behavior to a GameObject.
    2. In the object prefabs array set the prefabs you want to be pooled and reused throughout the game.
    3. In the amount to buffer array, specify how many of each object you want to instantiate on the scene start so you have pooled objects to start with. If you don't want to specify each, the default buffer amount will just be used.
    4. In your game call ObjectPool.instance.GetObjectForType(objectType,onlyPooled). For the object type just give the name of the prefab you want to spawn. Specify true or false for the onlyPooled value, if true it will only return a GameObject if there is already an object pooled, if false it will instantiate a new object if one is not available. (Set true if you want to limit how many objects are pooled, very helpful if you want a limited number of effects to ever happen at one time).
    5. Make sure your Gameobject you get from the pool knows when to add itself back into the pool if removed.


    I have also included the Effect.cs and SoundEffect.cs I use in my game to get anyone started right away. The Effect.cs behavior should be added to a game object, then have any number of ParticleEmitters added to it which will run a single emit each time StartEffect is called. Then after a set amount of time it will reset the effect and pool itself. SoundEffect, does a similar thing but with sound effects.


    I hope this can help people out who need to limit the number of instantiate calls made with reusable objects.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class ObjectPool : MonoBehaviour
    6. {
    7.    
    8.     public static ObjectPool instance;
    9.    
    10.     /// <summary>
    11.     /// The object prefabs which the pool can handle.
    12.     /// </summary>
    13.     public GameObject[] objectPrefabs;
    14.    
    15.     /// <summary>
    16.     /// The pooled objects currently available.
    17.     /// </summary>
    18.     public List<GameObject>[] pooledObjects;
    19.    
    20.     /// <summary>
    21.     /// The amount of objects of each type to buffer.
    22.     /// </summary>
    23.     public int[] amountToBuffer;
    24.    
    25.     public int defaultBufferAmount = 3;
    26.    
    27.     /// <summary>
    28.     /// The container object that we will keep unused pooled objects so we dont clog up the editor with objects.
    29.     /// </summary>
    30.     protected GameObject containerObject;
    31.    
    32.     void Awake ()
    33.     {
    34.         instance = this;
    35.     }
    36.    
    37.     // Use this for initialization
    38.     void Start ()
    39.     {
    40.         containerObject = new GameObject("ObjectPool");
    41.        
    42.         //Loop through the object prefabs and make a new list for each one.
    43.         //We do this because the pool can only support prefabs set to it in the editor,
    44.         //so we can assume the lists of pooled objects are in the same order as object prefabs in the array
    45.         pooledObjects = new List<GameObject>[objectPrefabs.Length];
    46.        
    47.         int i = 0;
    48.         foreach ( GameObject objectPrefab in objectPrefabs )
    49.         {
    50.             pooledObjects[i] = new List<GameObject>(); 
    51.            
    52.             int bufferAmount;
    53.            
    54.             if(i < amountToBuffer.Length) bufferAmount = amountToBuffer[i];
    55.             else
    56.                 bufferAmount = defaultBufferAmount;
    57.            
    58.             for ( int n=0; n<bufferAmount; n++)
    59.             {
    60.                 GameObject newObj = Instantiate(objectPrefab) as GameObject;
    61.                 newObj.name = objectPrefab.name;
    62.                 PoolObject(newObj);
    63.             }
    64.            
    65.             i++;
    66.         }
    67.     }
    68.    
    69.     /// <summary>
    70.     /// Gets a new object for the name type provided.  If no object type exists or if onlypooled is true and there is no objects of that type in the pool
    71.     /// then null will be returned.
    72.     /// </summary>
    73.     /// <returns>
    74.     /// The object for type.
    75.     /// </returns>
    76.     /// <param name='objectType'>
    77.     /// Object type.
    78.     /// </param>
    79.     /// <param name='onlyPooled'>
    80.     /// If true, it will only return an object if there is one currently pooled.
    81.     /// </param>
    82.     public GameObject GetObjectForType ( string objectType , bool onlyPooled )
    83.     {
    84.         for(int i=0; i<objectPrefabs.Length; i++)
    85.         {
    86.             GameObject prefab = objectPrefabs[i];
    87.             if(prefab.name == objectType)
    88.             {
    89.                
    90.                 if(pooledObjects[i].Count > 0)
    91.                 {
    92.                     GameObject pooledObject = pooledObjects[i][0];
    93.                     pooledObjects[i].RemoveAt(0);
    94.                     pooledObject.transform.parent = null;
    95.                     pooledObject.SetActiveRecursively(true);
    96.                    
    97.                     return pooledObject;
    98.                    
    99.                 } else if(!onlyPooled) {
    100.                     return Instantiate(objectPrefabs[i]) as GameObject;
    101.                 }
    102.                
    103.                 break;
    104.                
    105.             }
    106.         }
    107.            
    108.         //If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true
    109.         return null;
    110.     }
    111.    
    112.     /// <summary>
    113.     /// Pools the object specified.  Will not be pooled if there is no prefab of that type.
    114.     /// </summary>
    115.     /// <param name='obj'>
    116.     /// Object to be pooled.
    117.     /// </param>
    118.     public void PoolObject ( GameObject obj )
    119.     {
    120.         for ( int i=0; i<objectPrefabs.Length; i++)
    121.         {
    122.             if(objectPrefabs[i].name == obj.name)
    123.             {
    124.                 obj.SetActiveRecursively(false);
    125.                 obj.transform.parent = containerObject.transform;
    126.                 pooledObjects[i].Add(obj);
    127.                 return;
    128.             }
    129.         }
    130.     }
    131.    
    132. }
    133.  
    134.  

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Effect : MonoBehaviour
    5. {
    6.     /// <summary>
    7.     /// The array of emitters to fire when the effect starts.
    8.     /// </summary>
    9.     public ParticleEmitter[] emitters;
    10.    
    11.     /// <summary>
    12.     /// The length of the effect in seconds.  After which the effect will be reset and pooled if needed.
    13.     /// </summary>
    14.     public float effectLength = 1f;
    15.    
    16.    
    17.     /// <summary>
    18.     /// Should the effect be added to the effects pool after completion.
    19.     /// </summary>
    20.     public bool poolAfterComplete = true;
    21.    
    22.  
    23.    
    24.     /// <summary>
    25.     /// Resets the effect.
    26.     /// </summary>
    27.     public virtual void ResetEffect ()
    28.     {
    29.         if(poolAfterComplete)
    30.         {
    31.             ObjectPool.instance.PoolObject(gameObject);
    32.         } else {
    33.             Destroy(gameObject);
    34.         }
    35.     }
    36.    
    37.     /// <summary>
    38.     /// Starts the effect.
    39.     /// </summary>
    40.     public virtual void StartEffect ()
    41.     {
    42.         foreach ( ParticleEmitter emitter in emitters )
    43.         {
    44.             emitter.Emit();
    45.         }
    46.        
    47.         StartCoroutine(WaitForCompletion());
    48.     }
    49.    
    50.     public IEnumerator WaitForCompletion ()
    51.     {
    52.         //Wait for the effect to complete itself
    53.         yield return new WaitForSeconds(effectLength);
    54.        
    55.         //Reset the now completed effect
    56.         ResetEffect();
    57.        
    58.     }
    59.    
    60.    
    61.    
    62. }
    63.  
    64.  

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class SoundEffect : MonoBehaviour
    5. {
    6.    
    7.     /// <summary>
    8.     /// The sound source that will be played when the effect is started.
    9.     /// </summary>
    10.     public AudioSource soundSource;
    11.    
    12.     /// <summary>
    13.     /// The sound clips that will randomly be played if there is more than 1.
    14.     /// </summary>
    15.     public AudioClip[] soundClips;
    16.    
    17.     /// <summary>
    18.     /// The length of the effectin seconds.
    19.     /// </summary>
    20.     public float effectLength = 1f;
    21.    
    22.     /// <summary>
    23.     /// Should the effect be pooled after its completed.
    24.     /// </summary>
    25.     public bool poolAfterComplete = true;
    26.    
    27.    
    28.    
    29.     /// <summary>
    30.     /// Resets the effect.
    31.     /// </summary>
    32.     public virtual void ResetEffect ()
    33.     {
    34.         if(poolAfterComplete)
    35.         {
    36.             ObjectPool.instance.PoolObject(gameObject);
    37.         } else {
    38.             Destroy(gameObject);
    39.         }
    40.     }
    41.    
    42.     /// <summary>
    43.     /// Starts the effect.
    44.     /// </summary>
    45.     public virtual void StartEffect ()
    46.     {
    47.         soundSource.PlayOneShot(soundClips[Random.Range(0,soundClips.Length)]);
    48.        
    49.         StartCoroutine(WaitForCompletion());
    50.     }
    51.    
    52.     public IEnumerator WaitForCompletion ()
    53.     {
    54.         //Wait for the effect to complete itself
    55.         yield return new WaitForSeconds(effectLength);
    56.        
    57.         //Reset the now completed effect
    58.         ResetEffect();
    59.        
    60.     }
    61.    
    62.    
    63. }
    64.  
    65.  
     
  2. Bravini

    Bravini

    Joined:
    Nov 15, 2010
    Posts:
    9
    very handy, can't wait to try it out! Thanx!
     
  3. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Really good idea for a component. Great work!
     
  4. pakfront

    pakfront

    Joined:
    Oct 6, 2010
    Posts:
    551
    Looks great. Any ideas on how to rework it for Network use (to replace NetworkInstantiate, etc)? Would a simple RPCMode.Other call of the same function always result in the object with the same NetworkID being 'unpooled'?
     
  5. Hakimo

    Hakimo

    Joined:
    Apr 29, 2010
    Posts:
    316
    Wow I never knew about this. Umm, correct me if I'm wrong but can this actually be used to manage loading assets to a game? In the iOS, my app crashes when it's loading up to 13 assets. Can I actually use this to manage instantiation and destroying objects?

    -Hakimo
     
  6. AizenSousuke

    AizenSousuke

    Joined:
    Jul 10, 2010
    Posts:
    4
    Nice one. I'll be happy if someone could report the use on iOS / Android devices? Does this makes the game better optimized or not?
     
  7. nventimiglia

    nventimiglia

    Joined:
    Sep 20, 2011
    Posts:
    153
    Code (csharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5.  
    6. /// <summary>
    7. /// Repository of commonly used prefabs.
    8. /// </summary>
    9. [AddComponentMenu("Gameplay/ObjectPool")]
    10. public class ObjectPool : MonoBehaviour {
    11.  
    12.     public static ObjectPool instance { get; private set; }
    13.  
    14.     #region member
    15.     /// <summary>
    16.     /// Member class for a prefab entered into the object pool
    17.     /// </summary>
    18.     [Serializable]
    19.     public class ObjectPoolEntry {
    20.         /// <summary>
    21.         /// the object to pre instantiate
    22.         /// </summary>
    23.         [SerializeField]
    24.         public GameObject Prefab;
    25.  
    26.         /// <summary>
    27.         /// quantity of object to pre-instantiate
    28.         /// </summary>
    29.         [SerializeField]
    30.         public int Count;
    31.     }
    32.     #endregion
    33.  
    34.     /// <summary>
    35.     /// The object prefabs which the pool can handle
    36.     /// by The amount of objects of each type to buffer.
    37.     /// </summary>
    38.     public ObjectPoolEntry[] Entries;
    39.  
    40.     /// <summary>
    41.     /// The pooled objects currently available.
    42.     /// Indexed by the index of the objectPrefabs
    43.     /// </summary>
    44.     [HideInInspector]
    45.     public List<GameObject>[] Pool;
    46.  
    47.     /// <summary>
    48.     /// The container object that we will keep unused pooled objects so we dont clog up the editor with objects.
    49.     /// </summary>
    50.     protected GameObject ContainerObject;
    51.  
    52.     void OnEnable()
    53.     {
    54.         instance = this;
    55.     }
    56.  
    57.     // Use this for initialization
    58.     void Start()
    59.     {
    60.         ContainerObject = new GameObject("ObjectPool");
    61.  
    62.         //Loop through the object prefabs and make a new list for each one.
    63.         //We do this because the pool can only support prefabs set to it in the editor,
    64.         //so we can assume the lists of pooled objects are in the same order as object prefabs in the array
    65.         Pool = new List<GameObject>[Pool.Length];
    66.        
    67.         for (int i = 0; i < Entries.Length; i++)
    68.         {
    69.             var objectPrefab = Entries[i];
    70.        
    71.             //create the repository
    72.             Pool[i] = new List<GameObject>();  
    73.  
    74.             //fill it
    75.             for (int n = 0; n < objectPrefab.Count; n++)
    76.             {
    77.  
    78.                 var newObj = Instantiate(objectPrefab.Prefab) as GameObject;
    79.  
    80.                 newObj.name = objectPrefab.Prefab.name;
    81.  
    82.                 PoolObject(newObj);
    83.             }
    84.         }
    85.     }
    86.  
    87.  
    88.  
    89.     /// <summary>
    90.     /// Gets a new object for the name type provided.  If no object type exists or if onlypooled is true and there is no objects of that type in the pool
    91.     /// then null will be returned.
    92.     /// </summary>
    93.     /// <returns>
    94.     /// The object for type.
    95.     /// </returns>
    96.     /// <param name='objectType'>
    97.     /// Object type.
    98.     /// </param>
    99.     /// <param name='onlyPooled'>
    100.     /// If true, it will only return an object if there is one currently pooled.
    101.     /// </param>
    102.     public GameObject GetObjectForType(string objectType, bool onlyPooled)
    103.     {
    104.  
    105.         for (int i = 0; i < Entries.Length; i++)
    106.         {
    107.             var prefab = Entries[i].Prefab;
    108.  
    109.             if (prefab.name != objectType)
    110.                 continue;
    111.  
    112.             if (Pool[i].Count > 0)
    113.             {
    114.  
    115.                 GameObject pooledObject = Pool[i][0];
    116.  
    117.                 Pool[i].RemoveAt(0);
    118.  
    119.                 pooledObject.transform.parent = null;
    120.  
    121.                 pooledObject.SetActiveRecursively(true);
    122.  
    123.                 return pooledObject;
    124.             }
    125.             if (!onlyPooled)
    126.             {
    127.                 return Instantiate(Entries[i].Prefab) as GameObject;
    128.             }
    129.         }
    130.  
    131.         //If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true
    132.         return null;
    133.     }
    134.  
    135.     /// <summary>
    136.     /// Pools the object specified.  Will not be pooled if there is no prefab of that type.
    137.     /// </summary>
    138.     /// <param name='obj'>
    139.     /// Object to be pooled.
    140.     /// </param>
    141.     public void PoolObject(GameObject obj)
    142.     {
    143.  
    144.         for (int i = 0; i < Entries.Length; i++)
    145.         {
    146.             if (Entries[i].Prefab.name != obj.name)
    147.                 continue;
    148.  
    149.             obj.SetActiveRecursively(false);
    150.  
    151.             obj.transform.parent = ContainerObject.transform;
    152.  
    153.             Pool[i].Add(obj);
    154.  
    155.             return;
    156.         }
    157.     }
    158. }
     
  8. fish23

    fish23

    Joined:
    Apr 13, 2012
    Posts:
    1
    Hi,

    I am pretty new to Unity and found this Thread while I was searching for a possibility to pool GameObjects.
    I tried to used your Code and noticed that it doesn't work for me (NullpointerException at Pool = new List<GameObject>[Pool.Length];, which makes sense cause Pool does not exist when it tries to call Pool.Length).

    Anyway, I used your code and did some changes so it fits to my needs, e.g. destruction of dynamically instantiated objects when trying to pool them or use of arrays instead of Lists. Maybe someone likes to use it for his/her project.

    Code (csharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4.  
    5.  
    6. /// <summary>
    7. /// Repository of commonly used prefabs.
    8. /// </summary>
    9. [AddComponentMenu("Gameplay/ObjectPool")]
    10. public class ObjectPool : MonoBehaviour {
    11.  
    12.     public static ObjectPool instance { get; private set; }
    13.  
    14.     #region member
    15.     /// <summary>
    16.     /// Member class for a prefab entered into the object pool
    17.     /// </summary>
    18.     [Serializable]
    19.     public class ObjectPoolEntry {
    20.         /// <summary>
    21.         /// the object to pre instantiate
    22.         /// </summary>
    23.         [SerializeField]
    24.         public GameObject Prefab;
    25.  
    26.         /// <summary>
    27.         /// quantity of object to pre-instantiate
    28.         /// </summary>
    29.         [SerializeField]
    30.         public int Count;
    31.        
    32.         [HideInInspector]
    33.         public GameObject[] pool;
    34.        
    35.         [HideInInspector]
    36.         public int objectsInPool = 0;
    37.     }
    38.     #endregion
    39.  
    40.     /// <summary>
    41.     /// The object prefabs which the pool can handle
    42.     /// by The amount of objects of each type to buffer.
    43.     /// </summary>
    44.     public ObjectPoolEntry[] Entries;
    45.  
    46.     /// <summary>
    47.     /// The pooled objects currently available.
    48.     /// Indexed by the index of the objectPrefabs
    49.     /// </summary>
    50.  
    51.     /// <summary>
    52.     /// The container object that we will keep unused pooled objects so we dont clog up the editor with objects.
    53.     /// </summary>
    54.     protected GameObject ContainerObject;
    55.  
    56.     void OnEnable()
    57.     {
    58.         instance = this;
    59.     }
    60.  
    61.     // Use this for initialization
    62.     void Start()
    63.     {
    64.         ContainerObject = new GameObject("ObjectPool");
    65.  
    66.         //Loop through the object prefabs and make a new list for each one.
    67.         //We do this because the pool can only support prefabs set to it in the editor,
    68.         //so we can assume the lists of pooled objects are in the same order as object prefabs in the array
    69.        
    70.         for (int i = 0; i < Entries.Length; i++)
    71.         {
    72.             ObjectPoolEntry objectPrefab = Entries[i];
    73.        
    74.             //create the repository
    75.             objectPrefab.pool = new GameObject[objectPrefab.Count];
    76.  
    77.             //fill it                      
    78.             for (int n = 0; n < objectPrefab.Count; n++)
    79.             {
    80.                 GameObject newObj = (GameObject)Instantiate(objectPrefab.Prefab);
    81.                 newObj.name = objectPrefab.Prefab.name;
    82.                 PoolObject(newObj);
    83.             }
    84.         }
    85.     }
    86.  
    87.  
    88.  
    89.     /// <summary>
    90.     /// Gets a new object for the name type provided.  If no object type exists or if onlypooled is true and there is no objects of that type in the pool
    91.     /// then null will be returned.
    92.     /// </summary>
    93.     /// <returns>
    94.     /// The object for type.
    95.     /// </returns>
    96.     /// <param name='objectType'>
    97.     /// Object type.
    98.     /// </param>
    99.     /// <param name='onlyPooled'>
    100.     /// If true, it will only return an object if there is one currently pooled.
    101.     /// </param>
    102.     public GameObject GetObjectForType(string objectType, bool onlyPooled)
    103.     {
    104.  
    105.         for (int i = 0; i < Entries.Length; i++)
    106.         {
    107.             GameObject prefab = Entries[i].Prefab;
    108.  
    109.             if (prefab.name != objectType)
    110.                 continue;
    111.            
    112.             if (Entries[i].objectsInPool > 0)
    113.             {
    114.  
    115.                 GameObject pooledObject = Entries[i].pool[--Entries[i].objectsInPool];
    116.                 pooledObject.transform.parent = null;
    117.                 pooledObject.SetActiveRecursively(true);
    118.  
    119.                 return pooledObject;
    120.             } else if (!onlyPooled)
    121.             {
    122.                 GameObject obj = (GameObject)Instantiate(Entries[i].Prefab);
    123.                 obj.name = obj.name+"_not_pooled";
    124.                 return obj;
    125.             }
    126.         }
    127.  
    128.         //If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true
    129.         return null;
    130.     }
    131.  
    132.     /// <summary>
    133.     /// Pools the object specified.  Will not be pooled if there is no prefab of that type.
    134.     /// </summary>
    135.     /// <param name='obj'>
    136.     /// Object to be pooled.
    137.     /// </param>
    138.     public void PoolObject(GameObject obj)
    139.     {
    140.  
    141.         for (int i = 0; i < Entries.Length; i++)
    142.         {
    143.             if (Entries[i].Prefab.name != obj.name)
    144.                 continue;
    145.  
    146.             obj.SetActiveRecursively(false);
    147.             obj.transform.parent = ContainerObject.transform;
    148.             if (obj.rigidbody != null) {
    149.                 obj.rigidbody.velocity = Vector3.zero;
    150.             }
    151.                
    152.             Entries[i].pool[Entries[i].objectsInPool++] = obj;
    153.             return;
    154.         }
    155.         Destroy(obj);
    156.     }
    157. }
    Kind regards,
    fish23
     
  9. MMortal

    MMortal

    Joined:
    May 21, 2012
    Posts:
    58
    I am not sure how to call this. I am calling a Prefab called "spawnEasy"

    so if I call:

    Code (csharp):
    1. ObjectPool.instance.GetObjectForType(spawnEasy, true);
    I get three errors.

    "Cannot convert type 'UnityEngine.GameObject' to 'string'
    The best overloaded method match for 'ObjectPool.GetObjectForType(string,bool)' has some invalid arguments
    Argument '#1' cannot convert 'object' expression to type 'string'

    I tried to typecast it using: ((string)spawnEasy) but still got errors.

    I am sure it is how I am coding it but have no idea how to fix it. Using the prefab "easySpawn" and setting onlyPool to true, could somebody type this line correctly for me? Thank you very much for posting this code it is exactly what I was looking for.
     
  10. Pnikosis

    Pnikosis

    Joined:
    Jul 23, 2012
    Posts:
    4
    This should work:

    Code (csharp):
    1. ObjectPool.instance.GetObjectForType("spawnEasy", true);
    The string should be the object type.
     
  11. DexRobinson

    DexRobinson

    Joined:
    Jul 26, 2011
    Posts:
    594
    Sorry to bump this thread, but I was wondering how to you use this to destroy an object? Do you call ObjectPool(gameObject); to remove it from the world? Or should I use Destroy? I know Destroy uses up a lot of GC though.
     
  12. DexRobinson

    DexRobinson

    Joined:
    Jul 26, 2011
    Posts:
    594
    Okay I figured it out, you do use ObjectPool(gameobject), I do have another question though. For my game I am using this is spawn bullets. When I spawn the first set of bullets meaning when I am under the default buffer my bullets are fine, then when it loops back around they fire in the same direction they initial went. So if I fire the first bullet straight, when the loop goes back around, the first bullet in the next set goes straight even though I am looking to the left. I am using rigid bodies and adding force, I don't know if that has any effect on anything.
     
  13. Tochas

    Tochas

    Joined:
    Nov 24, 2012
    Posts:
    17
    Hi,

    I am new to Unity, but hava a solid background with c# and .net
    wouldn't be better/faster to use a Dictionary<string, GameObject> instead of two arrays?
    as the framework already provides a fast sort/compare algorithm for dictionary keys
     
    briveramelo likes this.
  14. PhobicGunner

    PhobicGunner

    Joined:
    Jun 28, 2011
    Posts:
    1,813
    OK, this was real simple to use, but a few modifications had to be made.

    1.) When you initialize the Pool, you wrote:
    Code (csharp):
    1. Pool = new List<GameObject>[ Pool.Length ];
    When what you probably meant to write was:
    Code (csharp):
    1. Pool = new List<GameObject>[ Entries.Length ];
    2.) I still ended up with a bunch of objects that would never pool themselves, and I found out it's because if there aren't enough objects in the pool (and onlyPool is false), it instantiates a new object and returns that. The issue is that it never renames the object, so it has "(Clone)" on the end of it's name, and since this name never matches any of the object types defined in my pool, it can never be pooled. This leads to a severe memory leak.
    So I modified that part of the code:
    Code (csharp):
    1.  
    2. if( !onlyPooled )
    3.             {
    4.                 GameObject newObj = Instantiate( Entries[ i ].Prefab ) as GameObject;
    5.                 newObj.name = Entries[ i ].Prefab.name;
    6.                 return newObj;
    7.             }
    8.  
    Otherwise, this was super simple to integrate and should be a lot less GC intensive on limited platforms (such as OUYA ;D)
     
    Ultroman likes this.
  15. PhobicGunner

    PhobicGunner

    Joined:
    Jun 28, 2011
    Posts:
    1,813
    On a side note, this seems to have reduced my overall CPU usage by about half. That's a rather big plus! :D
     
    briveramelo likes this.
  16. KV-2

    KV-2

    Joined:
    Oct 12, 2012
    Posts:
    1
    Hi
    how to change SetActiveRecursively,because in Unity 4 dont work
     
  17. Ecocide

    Ecocide

    Joined:
    Aug 4, 2011
    Posts:
    293
    It is still working in the current version of Unity 4 but it will probably be deleted in further versions.
    It is replaced by SetActive(boolean).
    Docs: http://docs.unity3d.com/Documentation/ScriptReference/GameObject.SetActive.html

    Just make sure every child of the game object you enable/disable with SetActive(true)/SetActive(false) is enabled in the first place. This way it behaves exactly like SetActiveRecursively.
     
  18. NCrush

    NCrush

    Joined:
    Aug 1, 2013
    Posts:
    5
    If I have already created and instantiated a pooled prefab as a game object in a trigger script attached to each new randomized instance,

    Code (csharp):
    1.  
    2. GameObject Module = ObjectPool.instance.GetObjectForType("Module"+rand.ToString(), false);
    3.  
    how do I call on that "Module"+rand.ToString() so that I can then assign it a new positioning co-ordinate if it already exists.

    In context:

    I have 3 prefabs made for a infinite runner scene and each prefab has a trigger script attached to it which spawns another pooled object -- BUT -- I want to decrease the memory footprint if the random number generated already exists, currently by just moving it's co-ordinates. The only thing is, I don't know how to reference "Module"+rand.ToString() using this script to be able to do so from each new trigger script if it already exists.

    Hope that makes sense.

    Thanks!
     
  19. Havok06

    Havok06

    Joined:
    Oct 4, 2013
    Posts:
    1
    I have to thank you a lot for making this simple script. It helped me reasonably understand the concept of Object pooling and I was able to adapt it to my game so it could run correctly on mobile devices.

    So I felt obliged to thank you.
     
  20. Dblfstr

    Dblfstr

    Joined:
    Jan 17, 2014
    Posts:
    15
    I too have just used this ObjectPool code. I had to do some tweaking so that my pooled objects would spawn at random heights, but this is way better than the one I had before.

    Here is the code snippet. You need to set values for heightMin and heightMax, and a transform to choose the postition.

    Code (csharp):
    1.  
    2. public GameObject GetObjectForType ( string objectType , bool onlyPooled )
    3.  
    4.     {
    5.  
    6.         for(int i=0; i<objectPrefabs.Length; i++)
    7.  
    8.         {
    9.  
    10.             GameObject prefab = objectPrefabs[i];
    11.  
    12.             if(prefab.name == objectType)
    13.  
    14.             {
    15.  
    16.                
    17.  
    18.                 if(pooledObjects[i].Count > 0)
    19.  
    20.                 {
    21.  
    22.                     GameObject pooledObject = pooledObjects[i][0];
    23.  
    24.                     pooledObjects[i].RemoveAt(0);
    25.  
    26.                     pooledObject.transform.parent = null;
    27.  
    28.                     pooledObject.SetActiveRecursively(true);
    29.  
    30.                    pooledObject.transform.position =  new Vector3(transform.position.x,
    31.  
    32.                                           transform.position.y+(Random.Range(heightMin,heightMax)),
    33.  
    34.                                           transform.position.z);
    35.  
    36.                     return pooledObject;
    37.  
    38.                    
    39.  
    40.                 } else if(!onlyPooled) {
    41.  
    42.                     return Instantiate(objectPrefabs[i]) as GameObject;
    43.  
    44.                 }
    45.  
    46.                
    47.  
    48.                 break;
    49.  
    50.                
    51.  
    52.             }
    53.  
    54.         }
    55.  
    56.            
    57.  
    58.         //If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true
    59.  
    60.         return null;
    61.  
    62.     }
    63.  
    64.  
     
    Last edited: Feb 19, 2014
  21. pokybluesky

    pokybluesky

    Joined:
    Feb 19, 2014
    Posts:
    3
    Code (JavaScript):
    1.  
    2. #pragma strict
    3.  
    4.  
    5.  
    6. import System.Collections.Generic;
    7.  
    8.  
    9.  
    10. static public var instance : ObjectPool;
    11.  
    12. public var objectPrefabs : GameObject[];
    13.  
    14.  
    15.  
    16. public var pooledObjects : List.<GameObject>[];
    17.  
    18. public var amountToBuffer : int[];
    19.  
    20. public var defaultBufferAmount : int = 3;
    21.  
    22.  
    23.  
    24. protected var containerObject : GameObject;
    25.  
    26.  
    27.  
    28. function Awake() {
    29.  
    30.     instance = this;
    31.  
    32. }
    33.  
    34.  
    35.  
    36. function Start () {
    37.  
    38.     containerObject = new GameObject("ObjectPool");
    39.  
    40.     pooledObjects = new List.<GameObject>[objectPrefabs.Length];
    41.  
    42.  
    43.  
    44.     var i : int = 0;
    45.  
    46.     for (var objectPrefab : GameObject in objectPrefabs) {
    47.  
    48.         pooledObjects = new List.<GameObject>();
    49.  
    50.         var bufferAmount : int;
    51.  
    52.      
    53.  
    54.         if (i < amountToBuffer.Length)
    55.  
    56.             bufferAmount = amountToBuffer;
    57.  
    58.         else
    59.  
    60.             bufferAmount = defaultBufferAmount;
    61.  
    62.      
    63.  
    64.         for (var n : int = 0; n < bufferAmount; n++) {
    65.  
    66.             var newObj : GameObject = Instantiate(objectPrefab) as GameObject;
    67.  
    68.             newObj.name = objectPrefab.name;
    69.  
    70.             PoolObject(newObj);
    71.  
    72.         }
    73.  
    74.      
    75.  
    76.         i++;
    77.  
    78.     }
    79.  
    80. }
    81.  
    82.  
    83.  
    84. function GetObjectForType( objectType : String, onlyPooled : boolean) {
    85.  
    86.     for (var i : int = 0; i < objectPrefabs.Length; i++) {
    87.  
    88.         var prefab : GameObject = objectPrefabs;
    89.  
    90.         if (prefab.name == objectType) {
    91.  
    92.          
    93.  
    94.  
    95.             if(pooledObjects.Count > 0)
    96.  
    97.                 {
    98.  
    99.                     var pooledObject : GameObject = pooledObjects[0];
    100.  
    101.                     pooledObjects.RemoveAt(0);
    102.  
    103.                     pooledObject.transform.parent = null;
    104.  
    105.                     pooledObject.SetActive(true);
    106.  
    107.                  
    108.  
    109.                     return pooledObject;
    110.  
    111.            
    112.  
    113.                 }  else if (!onlyPooled) {
    114.  
    115.                    return Instantiate(objectPrefabs) as GameObject;
    116.  
    117.          
    118.  
    119.                 }
    120.  
    121.  
    122.  
    123.  
    124.         }
    125.  
    126.     }
    127.  
    128.     return null;
    129.  
    130. }
    131.  
    132.  
    133.  
    134. function PoolObject(obj : GameObject) {
    135.  
    136.     for (var i : int = 0; i < objectPrefabs.Length; i++) {
    137.  
    138.         if (objectPrefabs.name == obj.name){
    139.  
    140.                 obj.SetActive(false);
    141.  
    142.                 obj.transform.parent = containerObject.transform;
    143.  
    144.                 pooledObjects.Add(obj);
    145.  
    146.                 return;
    147.  
    148.         }
    149.  
    150.  
    151.  
    152.     }
    153.  
    154.  
    155.  
    156. }
    Thank you for your scripts !! it's the best script ever..

    I did change your C# to Java Script .. Here it is
     
  22. Gab Steve

    Gab Steve

    Joined:
    Nov 3, 2012
    Posts:
    33
    I got the object pooler to move but i want the pooled objects to move as well... Or let the object pooler be still but the objects move at least? Anyway the point of this question is because i am making an infinite runner and someone suggested that i use a object pooling instead of instantiating. Any help is appreciated thanks - Gab.
     
  23. Locomalito

    Locomalito

    Joined:
    Apr 4, 2013
    Posts:
    3
    how to return objects in the pool using this class?
     
  24. AshwaniKumar

    AshwaniKumar

    Joined:
    Mar 19, 2013
    Posts:
    4
    have a blog post on this..You can check here.
     
  25. gintechsystems

    gintechsystems

    Joined:
    Feb 19, 2015
    Posts:
    3
    Hey all I am getting this error when stopping my game from the editor:

    Cannot change GameObject hierarchy while activating or deactivating the parent.
    UnityEngine.Transform:SetParent(Transform)
    ObjectPool:poolObject(GameObject) (at Assets/Scripts/ObjectPool.cs:169)
    DropletProperties:OnBecameInvisible() (at Assets/Scripts/GameTypes/DropletProperties.cs:50)

    I am not sure how to work around this, have tried checking if the parent is null or not, does not seem to fix the issue though.
     
  26. gintechsystems

    gintechsystems

    Joined:
    Feb 19, 2015
    Posts:
    3
    I was able to find a workaround by checking if the application quit using

    void OnApplicationQuit()
     
  27. Rawlin

    Rawlin

    Joined:
    Apr 1, 2015
    Posts:
    7
    Since I'm new to programming I really appreciate this script. It's working great. Thank you!

    I'm using a simple trigger to spawn enemies at pre specified locations.
    I'm including the script. The script is put on the object that is the trigger(with a 2D collider set to trigger) and the spawnlocation is just an empty gameobject set where it should spawn the enemy. Not sure if its the best way, but its working so far.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class TriggerObjectPool : MonoBehaviour {
    5.  
    6.     public Transform SpawnLocation;
    7.  
    8.     public void OnTriggerEnter2D(Collider2D other)
    9.     {
    10.         if (other.GetComponent<Player> () == null)
    11.             return;
    12.            
    13.         GameObject obj =  ObjectPool.instance.GetObjectForType("EnemyPrefab", true);
    14.  
    15.         if(obj == null) return;
    16.      
    17.         obj.transform.position = SpawnLocation.position;
    18.         obj.transform.rotation = SpawnLocation.rotation;
    19.         obj.SetActive (true);
    20.     }
    21.  
    22. }
    23.  
     
  28. adriandevera

    adriandevera

    Joined:
    May 18, 2013
    Posts:
    8
    Does anyone have an example with the use of dictionary? Specifically a key and vector3 for an object position.
     
  29. simonrauter

    simonrauter

    Joined:
    Jun 19, 2015
    Posts:
    7
    Somehow it is not working for me... Everything is ok except it keeps creating new objects. It never reuses deactivated ones. Can someone tell me what am I doing wrong?
     
  30. Ultroman

    Ultroman

    Joined:
    Mar 10, 2014
    Posts:
    110
    PhobicGunner wrote a post (scroll up) about some changes he needed to make, before it would actually be able to pool the created objects (the created objects have " (Clone)" added to their name, which makes them incompatible with the pool, so they are never put back into the pool).

    If that doesn't solve your problem, I don't really see how you can get the problem you describe.
     
  31. aksh2143

    aksh2143

    Joined:
    Jul 6, 2015
    Posts:
    10
    Dear, please help.
    i have used code something below.
    When pooled object touches an game object the last function "addtolist" is called.
    when i run game, a bug occures.
    in start the objects get pooled regularly but, after few seconds, a game object falls so fast that it overtakes the prevoiusly pooled object and again objects start poolig at normal speed.
    i think there should be fault in resetng object but i dont know what it is.
    anyone there?


    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;

    public class Poolingscript2 : MonoBehaviour {

    public GameObject[] allprefabs;
    public List<GameObject> bullets ;
    public int listsize;
    GameObject go;
    // Use this for initialization

    void Start ()
    {
    createlist ();
    InvokeRepeating ("fallobject", 2.0f,1.0f);
    }


    void createlist()
    {
    allprefabs = Resources.LoadAll<GameObject> ("Prefab");
    bullets = new List<GameObject> ();
    for (int i=0; i<listsize; i++)
    {
    foreach (GameObject allprifab in allprefabs)
    {
    go = Instantiate(allprifab)as GameObject;
    go.SetActive(false);
    bullets.Add (go);
    }
    }
    }


    GameObject temp;
    void fallobject()
    {
    int randm = Random.Range(0,listsize);
    temp = bullets.ElementAt(randm);
    bullets.Remove(temp);
    // temp.GetComponent<Rigidbody> ().mass = getdrag(dragmax);
    temp.transform.position = transform.position;
    //yield return new WaitForSeconds (10.0f);
    temp.SetActive(true);
    // Debug.Log ("removed element no: " +randm);
    }

    // GameObject g1;
    public void addtolist(GameObject g)
    {
    go = g as GameObject;
    go.SetActive (false);
    bullets.Add (go);
    }

    }
     

    Attached Files:

    • 1.jpg
      1.jpg
      File size:
      123 KB
      Views:
      1,228
    • 2.jpg
      2.jpg
      File size:
      115.8 KB
      Views:
      1,193
  32. Phimaka

    Phimaka

    Joined:
    Oct 8, 2016
    Posts:
    1
    This is my solution for reusing deactivated ones:

    // Return object to the pool

    public void ReturnObject(GameObject pooledObject)
    {
    for (int i = 0; i < objectPrefabs.Length; i++)
    {
    if (objectPrefabs.name == pooledObject.name)
    {
    pooledObjects.Add(pooledObject);
    pooledObject.SetActive(false);
    }
    }
    }
     
  33. HarleyK

    HarleyK

    Joined:
    Mar 27, 2015
    Posts:
    95
    hello i know this post is rather dead but using the object pooling script, i was wondering how i could implement it into the generation script i currently have.

    This script is randomly picking a prefab to spawn while picking a random spawn point from an array.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Generation : MonoBehaviour {
    6.  
    7.     // These are the spawn point arrays that are being used as placement for what's being spawned
    8.     public Transform[] terrainPoints;
    9.     public Transform[] obstaclePoints;
    10.     public Transform[] middlePoints;
    11.     // These are the actual list of prefabs that will be spawning
    12.     public GameObject[] terrainSpawn;
    13.     public GameObject[] obstacleSpawn;
    14.     public GameObject[] middleSpawn;
    15.     // These are the time intervals in which the spawnings will occur
    16.     public float terrainTimer = 2;
    17.     public float obstacleTimer = 2;
    18.     public float middleTimer = 2;
    19.     public float newTime = 2;
    20.     // This is the bool that determines whether the game will be pause (this is accessed by another script, not set by this one)
    21.     public bool isPause;
    22.     // This is accessing the level ring of the level, this isn't set in the inspector this is set dynamically
    23.     public GameObject theGround;
    24.  
    25.     public static Generation S;
    26.  
    27.     void Awake()
    28.     {
    29.         S = this;
    30.         theGround = GameObject.Find("level ring");
    31.     }
    32.    
    33.     // Update is called once per frame
    34.     void Update () {
    35.         // If isPause is set to false then we proceed to spawn the clones using the custom functions
    36.         if(isPause == false)
    37.         {
    38.             TerrainSpawn();
    39.             ObstacleSpawn();
    40.             MiddleSpawn();
    41.         }
    42.     }
    43.     // This is the function thats being called thats doing the spawning for terrain prefabs
    44.     void TerrainSpawn()
    45.     {
    46.         // This is taking the duration of time and decreasing it by a set frame
    47.         terrainTimer -= Time.deltaTime;
    48.         // If this is below or equal to zero, it spawns a new clone
    49.         if(terrainTimer <= 0)
    50.         {
    51.             // This randomly chooses an indexed prefab and transform and spawns something at those points
    52.             GameObject terrainClone = Instantiate(terrainSpawn[Random.Range(0, terrainSpawn.Length)], terrainPoints[Random.Range(0, terrainPoints.Length)].transform.position, transform.rotation);
    53.             terrainClone.transform.parent = theGround.transform;
    54.             // This resets the duration of time for the next spawn
    55.             terrainTimer = newTime;
    56.  
    57.         }
    58.     }
    59.     // This is the function called to spawn the obstacle clones
    60.     void ObstacleSpawn()
    61.     {
    62.         obstacleTimer -= Time.deltaTime;
    63.         if(obstacleTimer <= 0)
    64.         {
    65.             GameObject obstacleClone = Instantiate(obstacleSpawn[Random.Range(0, obstacleSpawn.Length)], obstaclePoints[Random.Range(0, obstaclePoints.Length)].transform.position, transform.rotation);
    66.             obstacleClone.transform.parent = theGround.transform;
    67.             obstacleTimer = newTime;
    68.         }
    69.     }
    70.     // This is the function being called to spawn clones for the middle section prefabs
    71.     void MiddleSpawn()
    72.     {
    73.         middleTimer -= Time.deltaTime;
    74.         if(middleTimer <= 0)
    75.         {
    76.             GameObject middleClone = Instantiate(middleSpawn[Random.Range(0, middleSpawn.Length)], middlePoints[Random.Range(0, middlePoints.Length)].transform.position, transform.rotation);
    77.             middleClone.transform.parent = theGround.transform;
    78.             middleTimer = newTime;
    79.         }
    80.     }
    81. }
    82.  
     
  34. Washburn

    Washburn

    Joined:
    Oct 13, 2015
    Posts:
    21
    Hi,
    would someone kindly edit the Ops script and re-post it so we have a fully functional pooler. I am not fluent enough to do such a thing and it seems a shame as its great.
    Also do we have to add things back into the pool ? if so would somebody be so kind as to show us noobs the solution.
    I am under the impression we dont have to remove anything from the pool just set its state to SetActive(false) and True as we need it.
     
  35. ruimcp95

    ruimcp95

    Joined:
    Mar 19, 2019
    Posts:
    5
    I know this is a dead thread, but for the noobies like myself, what OP means with step 5. is to call the method PoolObject() in the script that you use to specify the condition of deactivating the object pooled (f.e. when a bullet collides with an object).
    A simple example I have is, when my pooled prefab hits a collider, I call the method so it can put the prefab back in the "ObjectPool" hierarchy deactivated. This script is on my pooled prefab itself:


    Code (CSharp):
    1. private void OnTriggerEnter(Collider hitinfo)
    2.     {
    3.         ObjectPool.instance.PoolObject(gameObject);
    4.     }