Godot will not draw 2D objects out of view.
So, do not worry about GPU resources.
You can confirm this yourself. With the game running, go to the Debugger panel (you can find it on the bottom of Godot editor), and then the Monitor tab. On the left you will see a list of categories. You are looking for 2D. You should see both Items and Draw Calls reflect the objects that are in view. If there is nothing in view, they will fall to zero.
While we are at it, you might also be interested in the profiler. On the same Debugger panel, you will find a Profiler tab. If you enable it, it will show how much time different parts of the game take. Including time dedicated audio, physics, and to execute script functions.
If you are having performance problems, check the Profiler. You know what they say about premature optimization.
As you may have guessed, enemies outside of view still have a cost: Their scripts are still executing (and if they are moving their physics is being computed).
You might or might not want to have them executing. For sake of argument, let us say there are some enemies you want to stop when they are away form the player character.
You can enable or disable callbacks:
_process
with set_process
_input
with set_process_input
_physics_process
with set_physics_process
These functions take a bool
parameter. Pass true
to enable the callbacks (for example when the player character is nearby), and false
to disable them (when the player character is away).
And how to know if they are near the player character? Put a large detector Area2D
around the player character. Then you can use the "body_entered"
signal to enable them and the "body_exited"
signal to disable them.
As alternative to a large detector Area2D
, you can use get_world_2D().direct_space_state.intersect_shape
. See Physics2DDirectSpaceState
.
Yes, that means they still have their scripts and their _init
and _ready
will run. And they can also respond to signals. So, some animations, timers and similar might still be ticking.
Again, for the sake of argument, let us say you don't want that. What you need is to only load enemies near the player character. That can be with load
or with Background loading.
If we don't have loaded the instances, that also imply there is nothing for our detector Area2D
to detect per-se. So we need to modify our approach.
You might be interested in InstancePlaceholder
. You can create them by adding scene instance to the Scene panel, and the select "Load As Placeholder" from their context menu. However InstancePlaceholder
don't have a transform, so checking distance is not directly an option.
Let us see… We need something with a position. And ideally something for our detector Area2D
to detect. We can use another Area2D
as placeholder!
To the placeholder Area2D
attach an script with a func
that instances whatever necessary (e.g. perhaps it has InstancePlaceholder
s as children, and the func
calls replace_by_instance
on them) and you want to either have it remove itself (in which case you want to move the children to siblings first) or disable itself (by setting monitoring
and monitorable
to false). Then when we get the "area_entered"
signal call the that func
.
As you can imagine these placeholder Area2D
do not have to represent a single enemy. They can be responsible of triggering the load of multiple enemies. For example, a room full of enemies, that would otherwise be wasting resources being there out of view.
By the way, take advantage of collision layers and masks on both detector Area2D
and placeholder Area2D
so you can filter out any other areas you are not interested in. And, by the way, yes, you can do this with intersect_shape
too.