Loading...
Loading...
Use when building native extensions for Godot — godot-cpp (C++) or gdext (Rust), binding classes, building, and GDScript/C# interop
npx skill4agent add jame581/godotprompter gdextensionRelated skills: csharp-godot for when C# is enough, gdscript-advanced for GDScript performance idioms first, godot-optimization for profiling before going native, addon-development for distributing the result, export-pipeline for shipping the binaries.
⚠️ Changed in Godot 4.7: Custom text servers are no longer a GDExtension use case — TextServer GDExtension build support was removed, so a custommust be compiled into the engine as a C++ module. See GH-117056.TextServer
mkdir gdextension_example && cd gdextension_example
git init
# IMPORTANT: use the godot-cpp branch matching your target engine version (e.g. 4.3),
# not the literal "4.x".
git submodule add -b 4.3 https://github.com/godotengine/godot-cpp
cd godot-cpp && git submodule update --init && cd ..gdextension_example/
├── project/ # demo project to test the extension
│ └── bin/example.gdextension
├── godot-cpp/ # C++ bindings (submodule)
└── src/
├── register_types.{h,cpp}
└── gdexample.{h,cpp}scons platform=<platform>SConstructGodot 4.7+: Upstream's reference GDExtension interface files (e.g.) now live in the godot-headers repository instead of godot-cpp (GH-115401). godot-cpp consumes them from there, so the submodule workflow above is unchanged — this only matters if you vendor the raw interface headers directly (e.g. for a custom language binding).gdextension_interface.h
gdexample.h#pragma once
#include <godot_cpp/classes/sprite2d.hpp>
namespace godot {
class GDExample : public Sprite2D {
GDCLASS(GDExample, Sprite2D)
private:
double time_passed = 0.0;
double amplitude = 10.0;
double speed = 1.0;
protected:
static void _bind_methods();
public:
void _process(double delta) override;
void set_amplitude(double p_amplitude);
double get_amplitude() const;
void set_speed(double p_speed);
double get_speed() const;
};
}gdexample.cpp_bind_methodsvoid GDExample::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_amplitude"), &GDExample::get_amplitude);
ClassDB::bind_method(D_METHOD("set_amplitude", "p_amplitude"), &GDExample::set_amplitude);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amplitude"), "set_amplitude", "get_amplitude");
ClassDB::bind_method(D_METHOD("get_speed"), &GDExample::get_speed);
ClassDB::bind_method(D_METHOD("set_speed", "p_speed"), &GDExample::set_speed);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed", PROPERTY_HINT_RANGE, "0,20,0.01"),
"set_speed", "get_speed");
ADD_SIGNAL(MethodInfo("position_changed",
PropertyInfo(Variant::OBJECT, "node"),
PropertyInfo(Variant::VECTOR2, "new_pos")));
}GDCLASS(Class, Parent)ClassDBClassDB::bind_method(D_METHOD("name", "arg"), &Class::method)ADD_PROPERTY(PropertyInfo(...), setter, getter)PROPERTY_HINT_RANGE"0,20,0.01"ADD_SIGNAL(MethodInfo("name", PropertyInfo(...), ...))emit_signal("position_changed", this, new_pos)⚠️ Changed in Godot 4.7: The GDExtension interface functionsandobject_cast_toare deprecated in favor ofclassdb_get_class_tag-based casts. Binding libraries (godot-cpp, gdext) handle this internally — but native code that calls these interface functions directly should migrate its cast paths. See GH-119254.is_class
register_types.cppvoid initialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) return;
GDREGISTER_CLASS(GDExample);
}
void uninitialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) return;
}
extern "C" {
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();
}
}.gdextensionproject/bin/example.gdextension[configuration]
entry_symbol = "example_library_init"
compatibility_minimum = "4.3"
reloadable = true
[libraries]
macos.debug = "res://bin/libgdexample.macos.template_debug.dylib"
macos.release = "res://bin/libgdexample.macos.template_release.dylib"
windows.debug.x86_64 = "res://bin/gdexample.windows.template_debug.x86_64.dll"
windows.release.x86_64 = "res://bin/gdexample.windows.template_release.x86_64.dll"
linux.debug.x86_64 = "res://bin/libgdexample.linux.template_debug.x86_64.so"
linux.release.x86_64 = "res://bin/libgdexample.linux.template_release.x86_64.so"extern "C"entry_symbolGDREGISTER_CLASSMODULE_INITIALIZATION_LEVEL_SCENE[libraries]platform.feature.archtemplate_debugtemplate_release[icons][dependencies]Godot 4.7+: The raw GDExtension interface adds refcount-aware construction and registration entry points —andclassdb_construct_object3(GH-118214). Binding libraries built against 4.7 headers use them automatically; they only concern code that calls the interface directly.classdb_register_extension_class6
reloadable = truecompatibility_minimumtemplate_release⚠️ Changed in Godot 4.7:changed itsObject.is_class()parameter type fromclasstoStringfor performance. GDScript is unaffected and C# gained a compatibility method, but GDExtension binaries compiled against the old signature rely on the engine's compatibility mapping — rebuild against bindings matching your target version when updating to 4.7. See the 4.7 migration guide.StringName
.gdextensionextends Node
func _ready():
var node := GDExample.new()
node.speed = 2.0
node.position_changed.connect(_on_position_changed)
add_child(node)
func _on_position_changed(node, new_pos):
print("%s is now at %s" % [node.get_class(), new_pos])using Godot;
public partial class Demo : Node
{
public override void _Ready()
{
var node = new GDExample(); // The native class is available like any Godot type.
node.Set("speed", 2.0);
node.Connect("position_changed", Callable.From<Node, Vector2>(OnPositionChanged));
AddChild(node);
}
private void OnPositionChanged(Node node, Vector2 newPos)
=> GD.Print($"{node.GetClass()} is now at {newPos}");
}SetConnectCallOther languages & debugging: Rust (gdext) · Debugging native code
_bind_methodsentry_symbol.gdextensionextern "C"compatibility_minimum[libraries]template_release