How should you handle pooling of 100s of items?

Hello,

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?

Thanks,
jrDev

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 ?.

1 Like

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.

Hello,

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?

Thanks,
jrDev

That’s the system I’ve used in the past.

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.

1 Like

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.

1 Like

Hey,

I appreciate this!

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.

Thanks,
jrDev

Hey,

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.

Thanks,
jrDev

Resources LoadAsunc returns a ResourceRequest that has a progress property, it also has a completed event so you won’t have to poĺl if its ready.

Also try to cache objects which you instantiate so you will minimize this operation.

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.

1 Like

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).

Let us know how you get on.

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.