Using any C++ library in Godot


Godot has change into probably the most widespread recreation
engines of the previous couple of years. It is free, open supply underneath the MIT license,
and sufficiently small to obtain and begin utilizing in minutes. Most Godot video games are
written in GDScript, the engine’s personal scripting language.

Sooner or later, although, many initiatives want one thing that already exists as
a C or C++ library: a simulation library, a database, a networking protocol,
a machine studying runtime. GDScript can not name native code, however Godot can
load it by way of GDExtension, and godot-cpp, the official C++
bindings, allows you to expose that code as common engine lessons. Writing the
C++ code is the straightforward half. The arduous half is the construct: godot-cpp has to match
your Godot model, and each library you add must be compiled for every
platform you ship to.

In this put up we give a brief tour of Godot, clarify how C++ extensions work,
and present the best way to convey C++ libraries right into a Godot recreation with Conan and
godot-cpp 10, now accessible in ConanHeart. As an instance we
will use flecs, an Entity
Component System library, to simulate 100,000 particles inside a Godot scene.

100,000 particles simulated with flecs inside a Godot scene, fleeing from the mouse cursor

A Quick Introduction to Godot

Godot is a common goal engine for 2D and 3D video games. Everything in a Godot
mission is constructed from two ideas:

  • Nodes are the fundamental constructing blocks. Each node has a kind (Sprite2D,
    Camera3D, AudioStreamPlayer, Timer…), a set of properties you may
    edit within the Inspector, and callbacks equivalent to _ready() or _process() that
    the engine calls in the course of the recreation loop.
  • Scenes are bushes of nodes saved to disk as .tscn recordsdata. A scene might be
    a personality, a menu or a complete stage, and scenes might be instanced inside
    different scenes.

Behavior is often added by attaching a script to a node. GDScript is a
Python-like language designed for the engine, and it’s nice for gameplay
logic as a result of adjustments present up instantly with no compile step.

What makes Godot attention-grabbing for C++ builders is that the engine itself is
written in C++, and it could load extensions written in C++ with out being
recompiled. A category that comes from one in every of these extensions turns into a daily
engine class: it exhibits up within the editor subsequent to the built-in nodes, with its
properties within the Inspector, and GDScript can use it like every other node. The
subsequent part explains how these extensions work.

Extending Godot with C++

There are two methods so as to add C++ code to Godot:

  • Engine
    modules

    are compiled into the engine itself. They have full entry to the
    internals, however it’s worthwhile to construct and ship your individual copy of Godot, together with
    the editor and export templates for each platform.
  • GDExtension
    hundreds a shared library (.dll, .so, .dylib, or .wasm on the net) into
    an official, unmodified Godot construct at runtime. The engine talks to the
    library by way of a steady C interface.

GDExtension is the really helpful strategy for many initiatives, and it’s what number of
widespread plugins are distributed at this time. Because the C interface is verbose to
use instantly, the Godot staff maintains
godot-cpp, a C++ library that
wraps it with an API very near the one used contained in the engine. It gives
a C++ class for each engine class, equivalent to Node2D, Sprite2D or Input.

Your personal lessons are common C++ code that derives from these lessons. A node
written with godot-cpp appears to be like like this:

#embrace 

namespace godot {

class MyNode : public Node2D {
    GDCLASS(MyNode, Node2D)

protected:
    static void _bind_methods() {}

public:
    void _process(double p_delta) override {
        // runs each body
    }
};

} // namespace godot

Since model 10.0, a single godot-cpp launch works with any Godot model
from 4.3 onwards. You choose one with the api_version construct choice, and
godot-cpp generates its C++ lessons from the API of that model. An
extension constructed for Godot 4.3 additionally works in newer variations, however not in older
ones, so that you often choose the oldest Godot model you wish to assist.

Build targets and have tags

There is yet one more idea it’s worthwhile to know earlier than constructing something.
godot-cpp is compiled for one in every of three targets, named after the Godot
builds that load the library:

  • template_debug: the default. Enables debug checks by way of the
    DEBUG_ENABLED definition. This library is loaded by the editor and by
    debug exports.
  • template_release: for launch exports, with the debug checks eliminated.
  • editor: for libraries which might be solely loaded by the editor.

Which library Godot hundreds is set at runtime by a small .gdextension
file. It maps function tags to library paths. The debug tag matches the
editor and debug exports, and the launch tag matches launch exports:

[configuration]
entry_symbol = "gdexample_library_init"
compatibility_minimum = "4.7"

[libraries]
macos.debug = "res://bin/libgdexample.template_debug.dylib"
macos.launch = "res://bin/libgdexample.template_release.dylib"
linux.debug = "res://bin/libgdexample.template_debug.so"
linux.launch = "res://bin/libgdexample.template_release.so"
home windows.debug = "res://bin/libgdexample.template_debug.dll"
home windows.launch = "res://bin/libgdexample.template_release.dll"

The regular workflow

The Godot
documentation

recommends including godot-cpp to your repository as a git submodule and
constructing it collectively together with your library utilizing SCons. That works nicely for a
first extension, however each mission finally ends up compiling its personal godot-cpp for
every goal, platform and structure, and any third occasion library you
wrap, equivalent to a physics engine or a machine studying runtime, must be
vendored and constructed with matching flags for each platform Godot exports to.

Both are precisely the form of downside Conan was constructed to resolve.

Managing the Dependencies with Conan

With the godot-cpp recipe in ConanHeart, godot-cpp turns into a daily
bundle. The two parameters mentioned above are Conan choices:

  • api_version: the Godot API model the bindings goal, from 4.3 to
    4.7 (the default).
  • goal: template_debug (the default), template_release or editor.

Each mixture is constructed as soon as after which reused by each mission that wants
it, as an alternative of being compiled inside every extension.

Your GDExtension turns into simply one other C++ mission with dependencies. Any of
the greater than 1,900 libraries in ConanCenter, or
one you bundle your self with a Conan
recipe
, might be
added subsequent to godot-cpp, and Conan builds all of them persistently for each
platform you goal.

A Practical Example: A Swarm of 100,000 Particles

To present how this works in apply, we’ll write a GDExtension that
registers a brand new Swarm node. It simulates 100,000 particles that flee from
the mouse cursor and bounce off the window edges, and attracts all of them in a
Godot scene.

The simulation runs on flecs, an
Entity Component System (ECS) library for C and C++. In an ECS, entities
are plain ids, parts are plain information structs connected to them, and
methods are capabilities that run over each entity that has a given set of
parts. Components of the identical sort are saved collectively in reminiscence, which
makes iterating over giant numbers of entities very quick. That is why ECS is
a preferred alternative for simulations, crowds or bullet hell video games. It can be
the form of work the place native code pays off, since updating this many
entities each body is way sooner in C++ than in GDScript.

You can discover the entire instance within the Conan examples2
repository
:

$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/examples/libraries/godot-cpp/gdextension

The src folder incorporates the extension code, and demo is a daily Godot
mission that hundreds it.

Declaring the dependencies

The conanfile.py requires godot-cpp and flecs from ConanHeart:

from conan import ConanFile
from conan.instruments.cmake import CMake, CMakeToolchain, cmake_layout


class GDExtensionExample(ConanFile):
    package_type = "shared-library"
    settings = "os", "compiler", "build_type", "arch"
    turbines = "CMakeDeps"

    def necessities(self):
        self.requires("godot-cpp/10.0.0")
        self.requires("flecs/4.1.6")

    def format(self):
        cmake_layout(self)

    def generate(self):
        tc = CMakeToolchain(self)
        # Godot picks the library to load by its construct "goal", so we identify
        # the output after the goal godot-cpp was constructed with
        tc.cache_variables["GODOTCPP_TARGET"] = str(self.dependencies["godot-cpp"].choices.goal)
        tc.generate()

    def construct(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.construct()

The solely Godot particular element is in generate(). We learn the goal
choice of the godot-cpp dependency and go it to CMake, so the identify of the
library all the time matches the godot-cpp binary it was linked in opposition to.

The CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
mission(gdexample LANGUAGES CXX)

find_package(godot-cpp REQUIRED CONFIG)
find_package(flecs REQUIRED CONFIG)

add_library(gdexample SHARED
    src/register_types.cpp
    src/swarm.cpp
)
target_link_libraries(gdexample PRIVATE godot-cpp flecs::flecs_static)

# Output as demo/bin/libgdexample.., the trail the
# demo/bin/gdexample.gdextension file factors Godot to. The generator
# expression prevents multi-config turbines from including a Release/ subfolder
set_target_properties(gdexample PROPERTIES
    OUTPUT_NAME "gdexample.${GODOTCPP_TARGET}"
    PREFIX "lib"
    LIBRARY_OUTPUT_DIRECTORY "$<1:${CMAKE_SOURCE_DIR}/demo/bin>"
    RUNTIME_OUTPUT_DIRECTORY "$<1:${CMAKE_SOURCE_DIR}/demo/bin>"
)

This is a very commonplace CMake mission. The extension is a shared
library that hyperlinks godot-cpp and flecs statically, so there’s a single
library file to ship. We write it straight into demo/bin so Godot finds it
with out an additional copy step.

Writing the node

The Swarm class derives from Node2D and owns the flecs globe. The
parts of every particle are plain structs. The GDCLASS macro provides the
boilerplate that Godot’s class system wants, and _bind_methods() declares
what Godot can see, on this case the depend and flee_radius properties.
Once the category is registered, they seem within the Inspector and can be utilized
from GDScript. This is a simplified view of the category:

struct Position { float x, y; };
struct Velocity { float x, y; };

class Swarm : public Node2D {
    GDCLASS(Swarm, Node2D)

    int depend = 100000;
    double flee_radius = 150.0;
    flecs::globe globe;
    ...

protected:
    static void _bind_methods() {
        ClassDB::bind_method(D_METHOD("set_count", "depend"), &Swarm::set_count);
        ClassDB::bind_method(D_METHOD("get_count"), &Swarm::get_count);
        ADD_PROPERTY(PropertyInfo(Variant::INT, "depend"), "set_count", "get_count");
        // ... and the identical for flee_radius
    }
    ...
};

The remainder of the node connects each worlds. _ready() creates one flecs
entity per particle and a flecs system that updates them. It additionally units up a
MultiMesh,
which pulls many situations of the identical mesh in a single draw name, as a result of
one Godot node per particle could be far too heavy for 100,000 of them.

Every body, _process() palms the mouse place to flecs, runs the methods
with globe.progress(), and copies the ensuing positions again into the
MultiMesh. Again, this can be a simplified view, and the complete code is within the
repository:

void Swarm::_ready() {
    // One entity per particle, with a Position and a Velocity part
    for (int i = 0; i < depend; i++) {
        globe.entity().set<Position>({ ... }).set<Velocity>({ ... });
    }

    // A system that runs for each entity with each parts
    globe.system<Position, Velocity>("Move").every([this](flecs::iter &it, size_t, Position &p, Velocity &v) {
        // flee from the mouse, transfer, and bounce off the window edges
    });

    // A MultiMeshInstance2D youngster node that attracts all of the particles
    ...
}

void Swarm::_process(double p_delta) {
    mouse = get_local_mouse_position();
    globe.progress(static_cast<float>(p_delta));

    // Copy the place of each entity into the MultiMesh buffer
    render_query.every([&](const Position &p, const Velocity &v) { ... });
    multimesh->set_buffer(buffer);
}

Registering the extension

Finally, register_types.cpp registers the category when Godot hundreds the
library:

void initialize_gdexample_module(ModuleInitializationLevel p_level) {
    if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        return;
    }
    GDREGISTER_RUNTIME_CLASS(Swarm);
}

We register Swarm with GDREGISTER_RUNTIME_CLASS. By default, the code of
a GDExtension class additionally runs contained in the editor, so _ready() and
_process() would begin the simulation while you’re enhancing the scene. A
runtime class is barely a placeholder within the editor: you may add it to a
scene and set its properties, however its code solely runs when the sport is
operating.

The similar file defines gdexample_library_init(), the entry level named in
the .gdextension file. It is a couple of strains of boilerplate that look the identical
in each extension.

Building and operating

With every little thing in place, constructing the extension is a single command:

$ conan construct . --build=lacking
...
[100%] Linking CXX shared library .../demo/bin/libgdexample.template_debug.dylib
[100%] Built goal gdexample

Conan resolves godot-cpp and flecs, downloads precompiled binaries from
ConanHeart once they exist on your configuration, builds the remaining from
supply, generates the CMake integration and eventually builds the extension.

Note: godot-cpp requires C++17. If your default profile makes use of an older
commonplace, which is the case for MSVC, add -s compiler.cppstd=17 to the
command.

Now begin Godot 4.7, click on “Import” within the Project Manager, and choose
demo/mission.godot. When the mission opens, Godot reads
bin/gdexample.gdextension, hundreds the library, and Swarm turns into accessible
like all built-in node. You can discover it within the “Create New Node” dialog,
underneath Node2D:

The predominant scene of the demo already incorporates a Swarm node. Selecting it
exhibits depend and flee_radius within the Inspector, the 2 properties we certain
in _bind_methods():

The Godot editor with the Swarm node selected and its Count and Flee Radius properties in the Inspector

Press Play to run the scene, and transfer the mouse over the window to push the
particles round. Then cease it, change depend or flee_radius within the
Inspector, and play it once more to see how the swarm behaves with extra particles
or a wider flee radius.

Conclusion

GDExtension and godot-cpp allow you to write engine lessons in C++, and Conan
takes care of constructing godot-cpp and every other C++ library your extension
wants. This can be an enormous benefit once you distribute the extension:
constructing it for each platform you ship to solely takes altering the settings
of the construct.

Note for Linux: By default, the extension hyperlinks libstdc++ dynamically,
so the goal system should present a model at the least as new because the one used
to construct it. For broad compatibility, construct the extension and its
dependencies in opposition to a toolchain and system libraries suitable with the
oldest distribution you propose to assist. Alternatively, hyperlink libstdc++
statically and conceal its symbols with a linker model script.

Try the complete
example

and test the godot-cpp documentation
to study extra about writing extensions. If you might have any suggestions or run into
any points, please tell us within the Conan GitHub
repository
.

Happy recreation improvement!

This put up was written with AI help and reviewed by people.



Source link