Attention
You are reading the latest
(unstable) version of this documentation, which may document features not available
or compatible with Godot 3.x.
Checking the stable version of the documentation...
Work in progress
Godot documentation is being updated to reflect the latest changes in version
4.0
. Some documentation pages may
still state outdated information. This banner will tell you if you're reading one of such pages.
The contents of this page are up to date. If you can still find outdated information, please open an issue.
GDExtension C++ example¶
Introduction¶
The C++ bindings for GDExtension are built on top of the C GDExtension API and provide a nicer way to "extend" nodes and other built-in classes in Godot using C++. This new system allows the extension of Godot to nearly the same level as statically linked C++ modules.
You can download the included example in the test folder of the godot-cpp repository on GitHub.
Setting up the project¶
There are a few prerequisites you'll need:
a Godot 4 executable,
a C++ compiler,
SCons as a build tool,
a copy of the godot-cpp repository.
See also Compiling as the build tools are identical to the ones you need to compile Godot from source.
You can download the godot-cpp repository from GitHub or let Git do the work for you. Note that this repository has different branches for different versions of Godot. GDExtensions will not work in older versions of Godot (only Godot 4 and up) and vice versa, so make sure you download the correct branch.
Note
To use GDExtension
you need to use the master
branch of godot-cpp,
which is only compatible with Godot 4.0 and follow this example.
If you are versioning your project using Git, it is a good idea to add it as a Git submodule:
mkdir gdextension_cpp_example
cd gdextension_cpp_example
git init
git submodule add -b master https://github.com/godotengine/godot-cpp
cd godot-cpp
git submodule update --init
Do make sure you clone recursively to pull in both repositories:
mkdir gdextension_cpp_example
cd gdextension_cpp_example
git clone -b master https://github.com/godotengine/godot-cpp
Note
If you decide to download the repository or clone it into your folder, make sure to keep the folder layout the same as we've setup here. Much of the code we'll be showcasing here assumes the project has this layout.
If you cloned the example from the link specified in the introduction, the submodules are not automatically initialized. You will need to execute the following commands:
cd gdextension_cpp_example
git submodule update --init
This will initialize the repository in your project folder.
Building the C++ bindings¶
Now that we've downloaded our prerequisites, it is time to build the C++ bindings.
The repository contains a copy of the metadata for the current Godot release, but if you need to build these bindings for a newer version of Godot, simply call the Godot executable:
godot --dump-extension-api extension_api.json
Place the resulting extension_api.json
file in the project folder and add
custom_api_file=<PATH_TO_FILE>
to the scons command
below.
To generate and compile the bindings, use this command (replacing <platform>
with windows
, linux
or macos
depending on your OS):
To speed up compilation, add -jN at the end of the SCons command line where N is the number of CPU threads you have on your system. The example below uses 4 threads.
cd godot-cpp
scons platform=<platform> -j4 custom_api_file=<PATH_TO_FILE>
cd ..
This step will take a while. When it is completed, you should have static
libraries that can be compiled into your project stored in godot-cpp/bin/
.
Note
You may need to add bits=64
to the command on Windows or Linux.
Creating a simple plugin¶
Now it's time to build an actual plugin. We'll start by creating an empty Godot project in which we'll place a few files.
Open Godot and create a new project. For this example, we will place it in a
folder called demo
inside our GDExtension's folder structure.
In our demo project, we'll create a scene containing a Node called "Main" and
we'll save it as main.tscn
. We'll come back to that later.
Back in the top-level GDExtension module folder, we're also going to create a
subfolder called src
in which we'll place our source files.
You should now have demo
, godot-cpp
, and src
directories in your GDExtension module.
Your folder structure should now look like this:
gdextension_cpp_example/
|
+--demo/ # game example/demo to test the extension
|
+--godot-cpp/ # C++ bindings
|
+--src/ # source code of the extension we are building
In the src
folder, we'll start with creating our header file for the
GDExtension node we'll be creating. We will name it gdexample.h
:
#ifndef GDEXAMPLE_H
#define GDEXAMPLE_H
#include <godot_cpp/classes/sprite2d.hpp>
namespace godot {
class GDExample : public Sprite2D {
GDCLASS(GDExample, Sprite2D)
private:
float time_passed;
protected:
static void _bind_methods();
public:
GDExample();
~GDExample();
void _process(float delta);
};
}
#endif
There are a few things of note to the above. We include sprite2d.hpp
which
contains bindings to the Sprite2D class. We'll be extending this class in our
module.
We're using the namespace godot
, since everything in GDExtension is defined
within this namespace.
Then we have our class definition, which inherits from our Sprite2D through a
container class. We'll see a few side effects of this later on. The
GDCLASS
macro sets up a few internal things for us.
After that, we declare a single member variable called time_passed
.
In the next block we're defining our methods, we have our constructor and destructor defined, but there are two other functions that will likely look familiar to some, and one new method.
The first is _bind_methods
, which is a static function that Godot will
call to find out which methods can be called and which properties it exposes.
The second is our _process
function, which will work exactly the same
as the _process
function you're used to in GDScript.
Let's implement our functions by creating our gdexample.cpp
file:
#include "gdexample.h"
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void GDExample::_bind_methods() {
}
GDExample::GDExample() {
// initialize any variables here
time_passed = 0.0;
}
GDExample::~GDExample() {
// add your cleanup here
}
void GDExample::_process(float delta) {
time_passed += delta;
Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 + (10.0 * cos(time_passed * 1.5)));
set_position(new_position);
}
This one should be straightforward. We're implementing each method of our class that we defined in our header file.
Note our _process
function, which keeps track of how much time has passed
and calculates a new position for our sprite using a sine and cosine function.
There is one more C++ file we need; we'll name it register_types.cpp
. Our
GDExtension plugin can contain multiple classes, each with their own header
and source file like we've implemented GDExample
up above. What we need now
is a small bit of code that tells Godot about all the classes in our
GDExtension plugin.
#include "register_types.h"
#include "gdexample.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/godot.hpp>
using namespace godot;
void initialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
ClassDB::register_class<GDExample>();
}
void uninitialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT example_library_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_interface, 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();
}
}
The initialize_example_module
and uninitialize_example_module
functions get
called respectively when Godot loads our plugin and when it unloads it. All
we're doing here is parse through the functions in our bindings module to
initialize them, but you might have to set up more things depending on your
needs. We call the function register_class
for each of our classes in our library.
The important function is the third function called example_library_init
.
We first call a function in our bindings library that creates an initilization object.
This object registrates the initialization and termination functions of the GDExtension.
Furthermore, it sets the level of initilization (core, servers, scene, editor, level).
At last, we need the header file for the register_types.cpp
named
register_types.h
.
#ifndef GDEXAMPLE_REGISTER_TYPES_H
#define GDEXAMPLE_REGISTER_TYPES_H
void initialize_example_module();
void uninitialize_example_module();
#endif // GDEXAMPLE_REGISTER_TYPES_H
Compiling the plugin¶
We cannot easily write by hand a SConstruct
file that SCons would use for
building. For the purpose of this example, just use
this hardcoded SConstruct file
we've
prepared. We'll cover a more customizable, detailed example on how to use these
build files in a subsequent tutorial.
Note
This SConstruct
file was written to be used with the latest