8 minute read

Nodes are the bread and butter of Godot, and one of its core concepts for working with the engine. But nodes aren’t everything, and in fact you may find them to be overkill for a lot of uses since their relationship to scenes and the scene tree is their main draw. This relationship is great for displaying sprites and models or plugging into input and frame events, but nodes do also come with overhead, both in terms of performance and just usability since they have to be added and removed from the scene tree to use, that you may not want if you instead just need a place to drop some code. So if you need something more lightweight, or maybe just something more purely code oriented, you may want to consider the RefCounted class.

As the name would suggest to those of you with a moderate amount of programming experience, the RefCounted class is a plain, straightforward object that uses reference counting for memory management. If you’re unfamiliar with the concept of reference counting, this that means Godot tracks how many objects are holding a reference to a given RefCounted object so that it knows when that object is no longer needed. In practice, this means that you can simply instantiate a RefCounted object, use it for however long you need to, and then just move on when done with it without having to manually free the object. Godot will clean things up for you - with a caveat that we’ll discuss later.

The RefCounted class forgoes any association with the scene tree, so there are no input events, such as _unhandled_input(), no _process() or _physics_process() functions, and no way to view the object in the editor. There’s also no _ready() function, so if you’re used to initializing your code there you’ll want to instead use the _init() function. What you get for all these missing features, though, is one of the most lightweight types in the engine. This makes it a great choice for anything you want to make that is purely code-oriented. In my own projects, this base class is typically is used for things like AI decision makers, containers for holding typed data, and miscellaneous utility functions.

To use the RefCounted class, you just extend from it like you would any other script, add a class_name, and then write your code:

class_name MyRefClass
extends RefCounted

func do_something():
    print('I did something!')

At runtime, you can then instantiate it using the class_name you assign to it and start using it:

var my_ref = MyRefClass.new()
my_ref.do_something()

Note that the use of class_name is technically optional, as you can load and reference the script directly, but I would generally not recommend that for most use cases and won’t cover it here.

From there, it’s time to get to work and write whatever code you need to write, but there is still that caveat on reference counting I hinted at earlier. As mentioned, Godot tracks how many references there are to RefCounted objects and will mark them for deallocation when they are no longer used, and this usually works as expected. Say you have a node in your game and its functionality is extended by some RefCounted component. While that node is active, Godot will see that the parent node has a reference to the RefCounted object, and it is therefore unsafe to destroy. When you later go to free that node, Godot will see that there is a RefCounted object with no references to it, is therefore not being used by anything, and will free it from memory, as expected.

But say you have two components on that node that hold a reference to each other. When the parent node is freed, Godot will see that both components are still being referenced by something and will not free them from memory. The same can also happen if you have a RefCounted object referencing itself. And all of this applies even if these cyclical references are separated by a few hops through your code. Since Godot is simply counting the number of references to a given object, hence the class name, it does not know that these objects are no longer reachable or usable and will not free them, resulting in a memory leak. To get around this, you can use the weakref function to get a reference to the object that will not keep it alive if all that remains are weak references, meaning that in the example of two objects referencing one another, everything will work as expected once the parent node is freed. Godot will see that the only references remaining are weak references, and free both objects.

This is probably not an issue you’re likely to run into often, but it is important to keep in mind as you use more RefCounted objects in your code so that you don’t run into any unexpected issues.

And that’s really all there is to say about this class. It’s a simple, lightweight option for running code in your game that doesn’t require direct access to the scene tree, and it’s definitely worth considering for your future projects.