A few questions about object pooling vs instantiate + destroy

Hello! I’ve been working on different systems in my game (3D RPG) now and a lot of them I’ve been able to resolve with object pooling which is great as I’ve heard it’s generally a better practice than instantiating new objects and then destroying them.

There are a few upcoming systems I want to work on though that I’m not convinced object pooling would be the best solution for though, and so I have some questions about object pooling:

1. Is it possible to object pool objects that have vastly different colliders? For example, if I make a furniture placement system with chairs, lanterns, bowls, tables, etc. Each item has a very different collider, some might have more colliders, and colliders of different types.

2. Can you pool objects with different sets of components? Kinda related to question #1, what is the chair in the furniture system has an interaction component. I would need to add and remove components as I go I guess?

3. Can you pool objects that have intricate systems of child objects? If I make a random dungeon generator that is room based, and each room is different and filled with different child objects, could the rooms be reasonably object pooled?

I totally understand how object pooling is the best choice for repeating identical/similar objects like projectiles, or even enemy spawns, but for something like this I’m not sure sure.

I’m considering using instantiate + destroy methods for my furniture and random dungeon system as it seems like a better choice, but I want to know if there are solutions I’m overlooking here.

Mostly I hesitate because I’ve heard that you should always avoid destroying objects if they can be re-used, but again, I’m just not sure how I would cleanly avoid that with the furniture and dungeon generation system.

Pooling just means re-use. You can pool any GameObject, with any configuration of components, without issue. For GameObjects the gist of it is SetActive(true) when you need it and SetActive(false) when you’re done with it.

Pooling is most beneficial for objects being rapidly created and destroyed, like projectiles and explosions. I don’t think you’d see much benefit, if any, from pooling furniture unless your dungeon was extremely dynamic.

4 Likes

Thanks for the reply! Well in terms of the furniture, it’s in the context of a player furniture placement system. So the player can put down the furniture pieces. Just in case that wasn’t clear from the original post :slight_smile:

I’m not sure how much of a crack-smokin’ interior decorator your player is gonna be, but generally I believe what Grozz is getting at is that pooling really only makes sense for things produced at (for example) the scale of a massive bullet hell game where enemies are spewing tens or hundreds of bullets per second and they are making little impact splashes everywhere they land.

3 Likes

If you were really going to use object pools for all these different objects, I’d set up a separate object pool for each one. You don’t just have 1 object pool that different objects all share, instead each unique prefab gets its own pool. There’s a lantern pool, a table pool, a chair pool, etc.

But as already discussed, how often are you really destroying these things? I doubt it is worth the hassle.

3 Likes

That was my thought as well, the only way I thought it would really work well would be to create a pool for each type of object but that seems like a fairly big hassle so for now I may just go with instantiate/destroy on these cases.

Thanks to everyone who helped out with this topic!

The biggest hassle is writing and maintaining the prefab reset code for each kind of pooled object in my opinion. This is important for objects like enemies with stats that change (health, armor, etc), or physics objects which may have been moving when “destroyed”. Some something like a table, you probably don’t have anything to reset, but not sure what you’re doing with these objects.

1 Like

Agreed. This is also the place where the most subtle hard-to-track errors crop up. Let’s say you pool some enemies, and one day you add an extra flag to change a behavior. You rip through your first level, all the enemies are brand-new and fresh, everything works, you commit it all you move on.

Days or weeks might pass before you stress test what happens when you load two levels end to end, which suddenly starts re-using pooled enemies, enemies who MIGHT have that new flag set, might not. Either way, your pooling code fails to reset that value because by then you had forgotten you were even pooling. That bug could take WEEKS to track down and replicate, because sometimes it happens, sometimes it doesn’t. BAD BAD BAD.

Keep it simple, don’t try to be “smart” with your code by prematurely optimizing.

If you one day notice a performance issue, ONLY then look into it. Remember:

DO NOT OPTIMIZE CODE RANDOMLY… always start by using the profiler:

Window → Analysis → Profiler

https://discussions.unity.com/t/841163/2

Optimizing UnityEngine.UI setups:

https://discussions.unity.com/t/846847/2

3 Likes

I hope no one minds me bumping this thread as I feel like one of the OP’s questions was left unresolved:

**

[quote]**
2. Can you pool objects with different sets of components?
**[/quote]
**

Let’s say we have a valid reason for wanting to use pooling for a number of different types of objects, each type having its own set of components. Let’s say for the purpose of discussion that there are 1000 different types of object we want to pool, each with its own set of components.

Is there then a way to use a generic object pool which we can then add or remove or adjust the necessary components as required? I think the answer is no, but I wanted to ask just in case anyone has any ideas before I set up all my separate pools (don’t worry, there aren’t really 1000).

Unity - Scripting API: GameObject.AddComponent states:

Adding and removing components repeatedly would completely defeat the point of an object pool.

Thanks for your reply @spiney199 . Very much appreciate your contributions to the forum, in particular with your addressables knowledge, you’ve really helped me with your posts.

Instantiation of a component must have similar overhead to gameObjects then. In theory, I suppose the idea would then be to have component pools for each type of required component, from which you can pull one to attach to your blank gameObject, and then release when releasing the gameObject.

If I understand correctly though, this still would not work due to the inability to remove components via script.

edit: or perhaps even the act of attaching pre-instantiated components to gameObjects has its own significant overhead? If that’s the case then that certainly answers my question, but I would be a bit surprised to find that this would have the same overhead as instantiation.

Components can only exist attached to game objects. You cannot have free-floating components. So your idea is unfortunately impossible at present.

You can remove them though. It’s just done via Object.Destroy. You just can’t ‘detach’ them, which is what I think you’re trying to say.

1 Like

Ah, I see! Thank you. That clears that up then, separate pools is the only way to go.

Or don’t pool at all…

Are you spawning / destroying at least one thing per frame on average without fail?

If not then you probably will NOT see a benefit, and now you get to debug your pooling system from now and into the future.

At least run the profiler and SEE if it is a problem first before you wreck your codebase with pooling.

1 Like

hehe…

Would another, potentially easier option be to just space out the instantiation over multiple frames? This could be done over a number of passes, either based on different types of objects, or just allocating a maximum amount of time per-frame it has before it holds off until the next frame.

2 Likes

That’s actually a pretty intensive use case… don’t see a lot of that here, so I tend to knee-jerk and screech that pooling isn’t likely to help, which for most cases it isn’t.

In your case I’m not sure, but keep in mind that pooling ONLY helps in the make / destroy phase of things.

Depending on how you set a pool up, it might help in the initial stutter phase, but that’s incidental and can often be achieved with a simple preload-all-the-things approach.

I have no idea what your memory footprint is but perhaps you can get most of what you need simply by setting faraway stuff inactive. It would still be in memory but wouldn’t be receiving any updates.

1 Like

Thanks both for your thoughts. Spiney - yes I tried splitting out the instantiation evenly over up to 8 seconds. All it does is make the lag noticeable for longer. Honestly - it works really well and I wouldn’t mind playing a game with this lag if it was good enough, but realistically it’s probably too much, so if there exists a zero lag solution, I’m gonna go after it.

Kurt - the world is huge, memory is the key bottleneck. That’s what caused me to build it with addressables, but as I mention, I’ve found the instantiation lag is too much unfortunately, definitely significant enough for me to look at pooling.

…As my idea has been verified as legit by you two legends, I’m going to edit out my trade secrets and go back to not mentioning it until I’ve had more time to get a headstart. I hope that’s not too poor etiquette or doesn’t seem too delusional. If either of you are ever curious enough to discuss via DM please do :slight_smile: