This post is about a pattern that will help to collect all your objects in one pool.
UPDATED implementation can be found at "Object pooling Rev. 2"
In a previous post, "Infinite 2D background in Unity,“ we showed a trick on how to make an endless background using one picture, approximately the same approach used to everything that can be involved in the game. The larger the game, the more difficult it is to simulate all the game objects in real-time, so everything that goes beyond the screen is usually hidden and deleted to save such limited and valuable computing power. At the same time, allocating and freeing memory is one of the most expensive operations for computing. Also, in languages with a garbage collector (C#), this becomes a matter of time for its service. Accordingly, with a sufficient amount of available RAM, an interesting approach is to reuse created, but not used objects, instead of deleting them; this is what the pattern called the Object Pooling does, which we will cover in this post.
Theory
The theoretical part well described in sufficient detail, and you can refer to this book.
Practice
The main tasks of our implementation will be simplicity and minimalism.
The implementation based on the previous article about the endless background, so if you want to repeat this example and have no idea what to do, you can start with it and make the changes below.
This script used in prefab is responsible for all object pools(there may be several)
usingSystem;usingSystem.Collections.Generic;usingUnityEngine;publicclassObjectPool:MonoBehaviour{// There are no support of Dictionary type for Inspector, so let's use this
[Serializable]publicclassPrefabData{publicstringname;publicGameObjectprefab;} [SerializeField]privateList<PrefabData>prefabDatas=null;// Prefabs for new object instances
privateDictionary<string,GameObject>prefabs=newDictionary<string,GameObject>();// Pools for free objects
privateDictionary<string,Queue<GameObject>>pools=newDictionary<string,Queue<GameObject>>();privatevoidAwake(){// Put information about our pools to Dictionary
foreach(varprefabDatainprefabDatas){prefabs.Add(prefabData.name,prefabData.prefab);pools.Add(prefabData.name,newQueue<GameObject>());}prefabDatas=null;}publicGameObjectGetObject(stringpoolName){// If there are free object in pool return it
if(pools[poolName].Count>0){returnpools[poolName].Dequeue();}// If object pool empty return new Instance
returnInstantiate(prefabs[poolName]);}publicvoidReturnObject(stringpoolName,GameObjectpoolObject){// Return object to pool
pools[poolName].Enqueue(poolObject);}}
usingUnityEngine;publicclassProjectile:MonoBehaviour{ [SerializeField]privatefloatspeed=0;// Link to the pool handler object
[SerializeField]publicObjectPoolobjectPool=null;// Pool name
publicconststringPoolName="Projectiles";privateVector2_moveVector=Vector2.zero;publicVector2MoveVector{set=>_moveVector=value;}voidUpdate(){transform.Translate(_moveVector.x*Time.deltaTime*speed,_moveVector.y*Time.deltaTime*speed,0,Space.World);}voidOnBecameInvisible(){// After object leave screen we return the object to the pool
gameObject.SetActive(false);objectPool.ReturnObject(PoolName,gameObject);}}
usingUnityEngine;publicclassFire:MonoBehaviour{// References to scene objects
[SerializeField]privateTransformleftCannon=null; [SerializeField]privateTransformrightCannon=null; [SerializeField]privateUnevirseHandleruniverse=null; [SerializeField]privateTransformspace=null;privatebool_rightCannon=false;// Link to the object pool
[SerializeField]privateObjectPoolobjectPool=null;voidUpdate(){if(Input.GetMouseButtonDown(0)){// Get an object from the pool
GameObjectpr=objectPool.GetObject(Projectile.PoolName);// Objects from pool require proper initialization
pr.transform.SetParent(space);pr.transform.SetPositionAndRotation(_rightCannon?rightCannon.position:leftCannon.position,_rightCannon?rightCannon.rotation:leftCannon.rotation);Projectileprpr=pr.GetComponent<Projectile>();prpr.MoveVector=universe.LookVector.normalized;prpr.objectPool=objectPool;pr.SetActive(true);_rightCannon=!_rightCannon;}}}
This script not related to pooling, but if you want to repeat this code then you can use it to change example from the previous post
usingUnityEngine;publicclassUnevirseHandler:MonoBehaviour{// References to scene objects
[SerializeField]privateCameramainCamera=null; [SerializeField]privateGameObjectship=null; [SerializeField]privateGameObjectspace=null;// The radius of a possible camera view
privatefloat_spaceCircleRadius=0;// Original background object dimensions
privatefloat_backgroundOriginalSizeX=0;privatefloat_backgroundOriginalSizeY=0;privateVector2_shipPosition=Vector2.zero;privateVector2_lookVector=Vector2.zero;privateVector2_stvp=Vector2.zero;publicVector2LookVector=>_lookVector;voidStart(){// Original Background Sizes
SpriteRenderersr=space.GetComponent<SpriteRenderer>();varoriginalSize=sr.size;_backgroundOriginalSizeX=originalSize.x;_backgroundOriginalSizeY=originalSize.y;// Camera height equal to the orthographic size
floatorthographicSize=mainCamera.orthographicSize;// Camera width equals to orthographic size multiplied by aspect
floatscreenAspect=(float)Screen.width/(float)Screen.height;_spaceCircleRadius=Mathf.Sqrt(orthographicSize*screenAspect*orthographicSize*screenAspect+orthographicSize*orthographicSize);// The final background sprite size should allow you to move by one basic background texture size in any direction + overlap the radius of the camera also in all directions
sr.size=newVector2(_spaceCircleRadius*2+_backgroundOriginalSizeX*2,_spaceCircleRadius*2+_backgroundOriginalSizeY*2);_shipPosition=ship.transform.position;}voidUpdate(){// Mouse coordinates in World space
_stvp=mainCamera.ScreenToWorldPoint(Input.mousePosition);// Direction to the mouse pointer
_lookVector=_stvp-_shipPosition;floatrotZ=Mathf.Atan2(_lookVector.y,_lookVector.x)*Mathf.Rad2Deg;ship.transform.rotation=Quaternion.Euler(0f,0f,rotZ-90);}}
Demonstration
In the result below, you can see that new objects are created only in the absence of free ones.
Conclusion
Unity does not offer us any reference implementation of object pools, and since this is a prevalent pattern, you can find a wide range of variations of it. Code, given in the article, does not have such a useful thing as pre-filling the pool. Also, it requires the double-entry of the pool name, which increases the complexity of the configuration. And in general, you can make a rather voluminous decision for the sake of additional syntactic sugar and functionality; however, for a start, we will focus on a minimalistic version. See you next time! =)