I have been using the Poolmanager asset to pool game objects I am going to reuse. Throughout the development I have been increasing the number of game objects and total amount of each I will be needing. This has now increased to hundreds of prepooled objects needed, probably over 2000 (I am making a tile based board game). The problem now is that when I load the level that has to pre pool the items, it takes a LONG time. What I want to know if anyone had tips on how they would handle this…I don’t want loading times over 1 minute?
I’m not sure this would suit your purpose, but you could initialise each pool with a primary limit and dynamically instantiate additional objects as required ?.
Yeah, I don’t know how the Poolmanager asset specifically works, but you only need to instantiate pooled objects ahead of time if you have a high expectation you will indeed be using them. For objects there is a good chance you won’t be using, you shouldn’t need them instantiated ahead of time. Just have the pool system instantiate them when needed and keep them around for later use.
Even for objects you expect to need, you might be instantiating too many at the start.
I appreciate the responses! I wonder if I should just Instantiate a small amount at beginning then instantiate the rest over multiple turn changes during gameplay? How do you guys pool?
The following script allows you to control multiple prefab pools which can be activated using the 'poolingClass’ element index. If an element’s ‘Dynamic List = true’, the pool list will automatically adapt when additional objects are required.
It’s not perfect but could serve as a starting point.
Example Script
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class PoolingClass {
public Transform prefabObject;
public int primaryLimit;
public bool dynamicList;
public List<Transform> poolList;
}
public class PoolingManager : MonoBehaviour {
// Public Static Class
public static PoolingManager Instance;
public List<PoolingClass> poolingCache;
// --
void Awake () {
// Validate Instance
if (Instance && Instance != this) {
Destroy(this);
}
else {
Instance = this;
}
}
// --
void Start () {
// Initialise Pooling Lists
InitialisePoolingLists();
}
// --
void InitialisePoolingLists () {
// Populate Pooling Lists
foreach (PoolingClass element in poolingCache) {
for (int i = 0; i < element.primaryLimit; i++) {
Transform clone = Instantiate(element.prefabObject, transform);
clone.gameObject.SetActive(false);
element.poolList.Add(clone);
}
}
}
// --
public Transform GetObjectFromPool(int index) {
// Return First Inactive Object
foreach (Transform clone in poolingCache[index].poolList) {
if (!clone.gameObject.activeInHierarchy) {
return clone;
}
}
// Extend Dynamic List
if (poolingCache[index].dynamicList) {
Transform clone = Instantiate(poolingCache[index].prefabObject, transform);
poolingCache[index].poolList.Add(clone);
return clone;
}
// Return Null
return null;
}
}
I suggest that for scene initialization you load only the required objects, and make sure to cache said objects (so you won’t have to use Resources.Load again) and while running the game itself use Resources.LoadAsync for every object which you don’t immediately require also, try to use Hashsets/Dictionaries instead of arrays/lists so that instantiating the object that you want would be O(1) instead of O(n) which is relatively inefficient.
The way I approach this is to have a free queue, and when something tries to get the object from the pool, I first check if the queue is empty. If it is then I enqueue a bunch of objects. Regardless, I dequeue and return the first item in the queue. The disadvantage of this is you need to also have a ‘return object’ function that gets called when the object is no longer needed.
I would be /very/ wary of the approach of iterating through a list until you find an object that is somehow marked available. As xCeas has alluded to, this gets more and more expensive as your object pool grows. At a certain point, iterating through the list will become more expensive than just churning memory by creating and destroying objects.
The plan right now is to pre pool about 15 of each object when the player clicks Start on the main menu, then add new items to pool if it goes over the count that I currently have.
Another thing related, is there a way to know how long pooling items can take so I can create a progress bar when it loads the scene Async? I seem to have the beach ball cursor when pooling items.
Loading the scene Async only loads the scene. Anything done in Awake or Start is done after the scene is loaded which is were most pools initialize and create the pool.
I try to create as little as possible for each pool at the start after the scene has loaded. Then once the pool has run out of objects I create only what is requested. Sometimes I create a pool with zero objects created, then the system creates the objects when I request them. One thing to take into account is the stack/queue/list/dictionary that you use for the pool should have an initial size that would account for the maximum amount used to avoid garbage when the collection needs to be resized.
Please note that the script was a quick example and would require a bit of work to be efficient. The points made by the other members should definitely be taken into account when creating your own routine(s).
All objects are separated into cathegories by two axes. One axis is appearance probability. It means how likely this object will show in current scene, 0 - unlikely player will see it, it somewhat like rare visual effect or bonus with drop probability 1% all alike; 1 - probably player will see it, 30 to 90 percent probability; 3 - it will definitely appear on screen, maybe from the beginning, like 90-100 percent. On the another axis is how heavy that object is. 1 for things like sound, sprite, 0 to 5 subobjects and no more than 1-3 hierarchy depth. 2 is average objects and 3 is heavy objects with a lot of scripts on them and deep nesting hierarchies. So there will be 9 squares with objects needs different pooling and loading strategies ranging from creating required amount of objects in the editor and loading them along with scene ready to use to dynamically instantiating and growing pool size. If your game is small enough you can go with 2 squares instead of 9. Choose optimal pooling and loading stategies for every square and implement them.