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:

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 godot-cpp branch that matches the version of Godot that you are targeting. For example, if you're targeting Godot 4.1, use the 4.1 branch, which is what is shown through out this tutorial.

The master branch is the development branch which is updated regularly to work with Godot's master branch.

Warning

Our long-term goal is that GDExtensions targeting an earlier version of Godot will work in later minor versions, but not vice-versa. For example, a GDExtension targeting Godot 4.2 should work just fine in Godot 4.3, but one targeting Godot 4.3 won't work in Godot 4.2.

However, GDExtension is currently experimental, which means that we may break compatibility in order to fix major bugs or include critical features. For example, GDExtensions created for Godot 4.0 aren't compatible with Godot 4.1 (see Updating your GDExtension for 4.1).

If you are versioning your project using Git, it is recommended to add it as a Git submodule:

mkdir gdextension_cpp_example
cd gdextension_cpp_example
git init
git submodule add -b 4.1 https://github.com/godotengine/godot-cpp
cd godot-cpp
git submodule update --init

Alternatively, you can also clone it to the project folder:

mkdir gdextension_cpp_example
cd gdextension_cpp_example
git clone -b 4.1 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:
    double time_passed;

protected:
    static void _bind_methods();

public:
    GDExample();
    ~GDExample();

    void _process(double 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(double delta) {
    time_passed += delta;

    Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 +