I’ve recently starting making a game in Godot for the first time. I have a background in software engineering and development in Unity.
Recently I’ve been playing around with a prototype where I need script-to-script communication and I wanted to enforce a contract between them. Let’s say I have an enum that I want two scripts to have access to. I have this enum defined in a separate script. I have two options:
Use the preload() macro to load the script with the enum as a resource
Introduce the script as an autoloaded global singleton
It seems to me that preload() seems to be the better choice by far. Using autoload would pollute the global namespace (especially for large projects) and is presumably never freed, always taking up memory. Additionally, according to the documentation, additional nodes are creating in the scene tree to access the autoload globals, causing additional overhead. So in what situation would I want to use autoloads over preloads?
Side note: It looks like using preload() also never unloads the resource. Using the ResourcePreloader node seems to circumvent this, according to this open GitHub issue.
Autoloads are good for systems that are required to be accessed by a lot of nodes, like a dedicated save manager, signal bus, or an audio manager etc. For your usecase, I believe creating a custom resource and referencing it in the nodes that require it may be the best choice. Resources are automatically unloaded from memory when nothing is left referencing them. And also they’re cached by path in memory, so you won’t load() a Resource multiple times even if you call load() function several times in different nodes.
So you can simply create a custom EnumList resource and reference it in the nodes that require it.
var enum_list: EnumList = load("res://path/to/enum_list.tres")
In the end an Enum is just a mini array which you can then add to other classes. The ‘base’ constructor Enum isnt used as an instance so I cant see why it would make a difference where it is created.
But if its an Enum thats going to be used by several scripts then Autoload is the safest way to go. Yes this creates a new node at runtime, however you will more than likely need more (depending on the project) so why not just create one anyway.
If you are doing it via a preload from another node , then that node still needs to exist before you try using it, so there is no real difference as far as I can see. Autoloads are there for that reason, Preloading will be harder to debug.
This seems like a great solution for the specified use case, thank you. I had looked away from load() as it seemed inefficient to load the resource multiple times, but I missed this part of the documentation. I’ll also look into .tres files, they seem interesting.
When the engine loads a resource from disk, it only loads it once. If a copy of that resource is already in memory, trying to load the resource again will return the same copy every time.
The ‘base’ constructor Enum isnt used as an instance so I cant see why it would make a difference where it is created.
It matters where it is created due to scope limitations. Objects defined in global scope do not get freed in memory and pollute the namespace, while objects defined in smaller scopes are not accessible everywhere. This is about striking a balance between these two
If you are doing it via a preload from another node , then that node still needs to exist before you try using it, so there is no real difference as far as I can see.
I agree with you here, however this use case is not loading another node as a resource. It is loading a gdscript as a resource, which does not require instantiating a node, as far as I’m aware.
Yes this creates a new node at runtime, however you will more than likely need more (depending on the project) so why not just create one anyway.
Unfortunately I disagree with this philosophy. It’s never a bad thing to consider even small performance enhancements. Especially at the beginning of a project, where small decisions can make large downstream impacts.
However as they are static singletons, they are only created once, and in fact with a Enum, it’s the instance in the classes that is taking up the memory.
I dont think (will happily be corrected) the base Enum definition will be sitting in memory. The compiler will just use it, to compile the instances of it that are used in the classes. These are structs in effect and do not end up on the heap, only the instances of them.
Therefore for Enums it shouldnt be an issue , and for anything else, its static and only one copy will ever exist. (Although technically Godot does allow an autoload to be loaded more than once).
If the way gdscript gets compiled is anything like C++, then I think you’re correct that enums don’t sit in memory. I think the part that sits in memory is the resource that defines the enum. It would be interesting to dig into this further.