+4 votes

This question is the top Google result for "godot pool", but offers no real details.

I understand GDScript does not garbage collect, and thus there won't be intermittent pauses due to GC. However, pooling can still be useful if you are creating a lot of objects at once: a large enemy wave, a massive amount of bullets, various visual effects, etc.

Is pooling a good idea in Godot? And if so, how can you 'reset' an objects state when selecting it from a "dead" pool? How can you ensure it does not draw processing power when dead?

My gut is to remove them from the scene tree to prevent unneeded processing, and adding them back in should mostly reset state due to calling _ready again. Though that won't reset any class vars that don't use onready.

Edit: Trying various ways of making a node remove itself from the scene is giving lackluster results. It feels like Godot is not built with pooling in mind, even though it seems like it would be essential for good performance.

in Engine by (684 points)
edited by

Not a solution but a question really. What if the objects are pooled "off-screen" - is godot smart enough not to waste cycles processing them because they're not visible?

Just wondering if there's any performance penalty in removing and then re-inserting the objects into the scene tree.

The term "engine" has lost its meaning for many developers. They tend to think that an engine was "that UI that you see when you open Unity", but that's not the case. An engine is a library that has the purpose of filtering out information that is irrelevant to the result before sending it to the graphics card / sound card / network device / physics solvers. So yes, Godot is an engine, so it is culling objects that aren't on screen.

That doesn't mean however that these objects didn't "waste cycles", since their location offscreen means that they don't need to be processed by the graphics card, but they still exist in your game world, so their scripts will still be called. Instead of putting them off screen remove them from the tree. That will keep them in memory but tell the engine to completely ignore them.

1 Answer

+5 votes
Best answer

Pooling in gameplay code is a concept, so it depends on what you want to pool, so there is no "best" way in general, but it's often common sense on a per case basis:

Pooling sprites or meshes requires to just hide them.
Pooling physics objects requires to remove them from all layers they are into.
Pooling sample players requires to stop all their sounds.
Pooling AnimationPlayers requires to stop their animation.
Pooling a scene instance which contains all of the above requires to take care of all of them, unless your gameplay decisions allows you to take some shortcuts.

Pooled nodes can stay in the tree, unless it's inconvenient for you, in which case you can indeed remove them from the tree (which also takes care of much special cases listed above, though it might be a bit slower).
However, if you do so (and even in other methods), you should have a list per type of object in which you put the "unused" objects, otherwise they will leak.

About removing nodes from tree: in the question you linked, I was removing a node from within a signal handler, which doesn't seem to be allowed currently. The solution is to use call_deferred to postpone the removal to the end of the frame.

In Godot 3, _ready is not called again when nodes get re-added to the tree: https://github.com/godotengine/godot/issues/17182
So you must think about pooling a little bit when you design your scenes.
Don't do too much fluff for pooling either, because that could defeat the purpose, and there is no efficient way of doing "general" pooling (which would end up being what the Godot internals do in the first place, which you want to avoid with your own pooling!).

Instancing an object from the pool again requires a custom spawning logic. It can range from calling show to having your own on_spawn function in which you do everything needed.
Another catch is that if you want to also preallocate objects to not have a first-creation stutter, you should not assume that _ready or _enter_tree means the object spawns, because depending on your pooling strategy they could get called when you preallocate them. Being "pooled" is like a "dead" state, it has to be taken into account in your logic.

And importantly, profile your game to see if pooling improves things, because there is no point doing that otherwise. Also, remember there are other ways to optimize things, like not using nodes in the first place and use servers directly.

by (28,740 points)
selected by

When usingget_parent().remove_child(self) to attempt to pool a colliding bullet, If I use call_deferred I get the error

0:00:10:0997 - Condition ' !body_in && !E ' is true.
----------
Type:Error
Description: 
Time: 0:00:10:0997
C Error: Condition ' !body_in && !E ' is true.
C Source: scene\2d\area_2d.cpp:161
C Function: Area2D::_body_inout

If I do not use call_deferred the error is

0:00:08:0625 - This function can't be used during the in/out signal.
----------
Type:Error
Description: This function can't be used during the in/out signal.
Time: 0:00:08:0625
C Error: Condition ' locked ' is true.
C Source: scene\2d\area_2d.cpp:324
C Function: Area2D::_clear_monitoring

Though they both seem to be working fine. Can I just suppress the error and move on? How risky would it be to do something like that?

What is your code with call_deferred?

    call_deferred("_unparent")

func _unparent():
    get_parent().remove_child(self)

this is the only thread I could find about this error, changing the mask did not help, but setting the position did. Really, really weird.

self.global_position = Vector2(-50,-50)
call_deferred("_unparent")
...

Jeez, that's pretty unintuitive...
It's not the first time I have to build convoluted workarounds when dealing with 2D physics though...

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.