I'm on
and
, anyone is welcome: feel free to get in contact if you need any help!
If you appreciate my work, consider buying me a coffee and help me go full-time!
GitHub Gists are great, but why not enjoy this guide with a dark theme and awesome styling like this? 👀
Grab the PDF here for free! 🥳 Let's get started!
I decided to start writing gists to keep track of my Godot plugin development journey
and to address what could be the issues beginners can encounter when they start.
Here's the Godot GDExtension doc page
to get started: follow the instructions and you should be good setting up an example plugin.
Now what? Where to go now?
I hope this series of gists can be some sort of guide about Godot architecture for the beginner plugin developers as I am now,
sharing good practices and the meaning behind them.
In this first part we will talk about how Godot initialize your plugin and where to register your custom classes.
If you set up your plugin following the doc page, you already created a .gdextension
file. Let's look at it's first lines of code:
[configuration]
entry_symbol = "example_library_init"
compatibility_minimum = "4.1"
The name of entry_symbol
is pretty didactic: you need to put the name of the entry function there.
Eventually you may want to change the name of the function from example
to your_plugin_name
, so be sure to change it also at its definition.
But, where's the definition?
This code at the end of register_types.cpp
is the entry function we were talking about, that initializes the GDExtension plugin we are building:
extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT example_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_example_module);
init_obj.register_terminator(uninitialize_example_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}
NOTE: If you're interested in the extern "C"
part, you may want to search for name mangling.
NOTE: If you want to change the name of the function to your plugin name, remember to also change the entry_symbol
value in the .gdextension
(otherwise it won't recognize the function and it won't be able to initialize your plugin).
The first thing it does is calling:
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
The syntax here shows the usage of a direct initialization, but it's basically creating a InitObject
type called init_obj
.
Then it calls:
init_obj.register_initializer(initialize_example_module);
init_obj.register_terminator(uninitialize_example_module);
These functions register initialize_example_module
and uninitialize_example_module
as callbacks so that they will be called at the right initialization and shutdown time.
To control what would be the right time to call these initialization functions let's look at the next line:
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
Let's have a look at the possible values for the only argument:
enum ModuleInitializationLevel {
MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
MODULE_INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE,
MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR,
MODULE_INITIALIZATION_LEVEL_MAX
};
ModuleInitializationLevel
is an enum
, and depending on the value, your extension will be loaded at different stages of initialization/shutdown.
The order of initialization is the same as they are listed in the enum
:
MODULE_INITIALIZATION_LEVEL_CORE
MODULE_INITIALIZATION_LEVEL_SERVERS
MODULE_INITIALIZATION_LEVEL_SCENE
MODULE_INITIALIZATION_LEVEL_EDITOR
NOTE: MODULE_INITIALIZATION_LEVEL_CORE
and MODULE_INITIALIZATION_LEVEL_SERVERS
will need the editor or game restart to take effect of any change.
TIP: MODULE_INITIALIZATION_LEVEL_SCENE
should be fine most of the time if you're in doubt.
This goes a long way, you can check the order of execution in the main.cpp.
Finally, the last line:
return init_obj.init();
init()
is responsible for submitting the data we talked about previously, so that the plugin can be initialized and deinitialized properly.
In the next part we will talk about the body of these two:
void initialize_example_module(ModuleInitializationLevel p_level);
void uninitialize_example_module(ModuleInitializationLevel p_level);
Here we will register all our custom classes so that they can be used by Godot and in GDScript. See you there!
and my Buy Me a Coffee page to receive updates when the next parts are released!
We need someone to address GDExtension C# bindings
godotengine/godot-proposals#8191