2
\$\begingroup\$

I would like to create a 2D Sidescrolling map, like one for a platformer, but for a shooter game.

At first I thought I could just draw a big map based on a tilemap and make the player and the enemies move around in it and just create a camera to follow the player, but now I am concerned Godot will try to render everything outside the camera and waste GPU resources. Is that the case? If so, how can I go about hiding the sprites off screen, would something like this work?:

if [$Sprite is outside camera]: 
    $Sprite.visible = false
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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

\$\endgroup\$
1
  • \$\begingroup\$ Wow, thanks. This was very helpful \$\endgroup\$
    – pion
    Jan 4, 2022 at 20:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .