Categories
godot performance

Godot Off-Screen Processing Control

This is part 3 of 4 in a series about some recent performance tuning I completed on Elevation TD.

Overview

Out of the box, Godot supports culling of off-screen object display – which is great, but if you have scripts attached to those culled-for-display-purposes-only objects, the scripts are still running, chewing up CPU for no displayable reason. Wouldn’t it be nice to mitigate that off-screen impact?

Godot gives you a way to exactly do that – its actually really easy to use, but you have to put a little thought into what you do with it. At any point in time, you can check to see if the node a script belongs to is within the camera’s frustum (i.e. in the camera’s field of view) via Camera.is_position_in_frustum(global_position) – see: https://docs.godotengine.org/en/stable/classes/class_camera3d.html#class-camera3d-method-is-position-in-frustum.

Frustum Checking

With a pointer to your main camera, you can call this method any time you want to see if your script is attached to an object that is visible with a a conditional (in my case, I have a static class called “StateMachine” that the Camera is referenced through):

if StateMachine.mainCamera.is_position_in_frustum(global_position):
  # do on screen activities
else:
  # do an off-screen version of activities

You want to be a bit careful about how often you check for this – don’t run it every frame. In the case of Elevation TD, I have enemies that perform a series of activities where it might be interesting to check their on-camera/off-camera status for:

  • Conduct a walking motion
  • Attack when in close proximity to targets
  • Do a little performance when they die

When these enemies are off-screen, I still need them to move, attack, search for targets, and die – the camera never has the whole battlefield so there’s pretty much always some enemies off-camera. However, I can periodically check to see if they are off-camera and then perform a lighter-weight version of each of these activities:

  • Don’t perform walking motion, just move to a next position
  • Don’t perform any of the visible parts of an attack, just damage the target
  • Don’t do a death performance, just die and remove yourself from the battlefield

So the enemy script checks if the enemy is off camera at the start of each step motion, when it attacks, and when it dies. You can see the difference in the graphic below – note the FPS when looking at all the enemies attacking vs the FPS when staring into the water:

Checking the camera’s frustum is an easy check – the harder part is coming up with lighter-weight versions of the scripts activities.

Bonus Round

Once you fold in some of these camera checks, it may occur to you that there are some other things you could check to govern whether to show a “full experience” vs a “lighter experience. For example, what’s the current FPS…

In the above image with the battle scene, one of the things you might not realize is that there are three versions of an enemy’s death:

  • Off-Camera – Just die and go away, no drama
  • On-Camera – Explode, particle effects, and other drama
  • On-Camera and FPS is below 90 – Just explode, provide a watered down version of drama

Doing this provides another protective layer to control how much CPU processing is going on under the hood. Like checking the camera frustum, you don’t want to do this on every frame, but it can be handy to control FPS impact of specific events.

To do this is a very simple check from the Engine API:

if Engine.get_frames_per_second() > 90:
  # perform extra dying acts for visual drama

The nice thing about this kind of check is it allows you to provide a more visually rich presentation when there are fewer actors fighting it out on the battlefield – which you want because the fewer the actors on the field, the closer the player will be looking at them. However, in a larger battle scene, it will tone down the visuals, but the player will largely not notice because focusing on any one actor in a battlefield of two hundred other actors is unlikely.

See the other parts of this series: