mcpField
G
gdextension

gdextension

github.com/jame581開発889152 回インストール4 日前に更新
リポジトリ

gdextension について

エンジンを再ビルドせずにGDExtensionでネイティブC++またはRustコードを追加するGodot開発者向けスキルです。クラスのバインド、共有ライブラリのビルド、GDScriptやC#からの利用を扱います。

主な機能

  1. 01ネイティブC++またはRustの共有ライブラリ
  2. 02メソッド、プロパティ、シグナルのバインド
  3. 03GDExtensionのビルドとエントリーポイント設定
  4. 04エンジンバージョンの互換性指針
  5. 05GDScriptとC#からの利用パターン

活用シーン

  1. 01性能計測済みの重要コードの高速化
  2. 02既存のC/C++ライブラリの呼び出し
  3. 03Godot向け言語バインディングの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jame581/godotprompter --skill gdextension

インストール後、Claude Code で/gdextensionを入力して読み込まれたか確認します。

同梱ファイルreferences/debugging-native.mdreferences/rust-gdext.md

詳細

GDExtension

エンジンを再コンパイルせずに、GodotでネイティブのC++(またはRust)を共有ライブラリとして実行できます。パフォーマンスが重要なコード、既存のC/C++ライブラリのラッピング、言語バインディングに使用します。

関連スキル: C#で十分な場合は csharp-godot、まずGDScriptのパフォーマンス向上イディオムを試す場合は gdscript-advanced、ネイティブ化する前にプロファイリングする場合は godot-optimization、成果物を配布する場合は addon-development、バイナリを出荷する場合は export-pipeline を参照してください。


1. GDExtensionを使うべき場面

ほとんどすべてのゲームロジックには、GDScriptまたはC#を使用してください。本当に必要な場合に限り、GDExtensionを選びます。

  • ネイティブの速度が必要な、GDScript/C#では処理が追いつかないホットループ(まずプロファイリングしてください。godot-optimization を参照)。
  • 直接呼び出す必要があるC/C++ライブラリのラッピング。
  • 言語バインディングの構築。

C++モジュールはエンジンに組み込まれてコンパイルされるため、カスタムエンジンバイナリの出荷が必要です。これに対して、GDExtensionの主な利点は、標準の Godotで動作し、共有ライブラリだけを配布すればよいことです。「GDScriptやC#よりも使い方が複雑」なため、デフォルトで選ぶべきではありません。

⚠️ Godot 4.7での変更点: エンジンに組み込まれているテキストサーバー(text_server_adv、text_server_fb)は、GDExtensionとしてビルドできなくなりました。gdextension_build/ スクリプトが削除されたためです。TextServerExtension をサブクラス化して独自のテキストサーバーをGDExtensionとして作成する方法には影響ありません。GH-117056 を参照してください。


2. プロジェクトとビルドの設定

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> でビルドします(現在のプラットフォームを対象にする場合はプラットフォームを省略します。デフォルトのビルドは debug です)。公式の SConstruct はここで手作業で作成するものではなく、C++チュートリアルからダウンロードできるファイルです。godot-cppのビルドドキュメントに従ってください。公式の方法はSConsですが、godot-cppはCMakeにも対応しています。

Godot 4.7以降: アップストリームのGDExtensionインターフェースのリファレンスファイル(例:gdextension_interface.h)は、godot-cppではなくgodot-headersリポジトリに置かれるようになりました(GH-115401)。godot-cppはそこからファイルを利用するため、上記のサブモジュールのワークフローに変更はありません。これは、生のインターフェースヘッダーを直接ベンダーとして取り込む場合(カスタム言語バインディングなど)にのみ関係します。


3. クラスのバインディング(C++)

ヘッダー(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_methods):

void 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) — すべてのネイティブクラス本体の先頭に記述します。型をGodotの ClassDB に登録します。
  • ClassDB::bind_method(D_METHOD("name", "arg"), &Class::method) — メソッド(および引数名)を公開し、GDScript/C#/エディターから呼び出せるようにします。
  • ADD_PROPERTY(PropertyInfo(...), setter, getter) — Inspectorプロパティを登録します。まずgetterとsetterをバインドし、その後ここで名前を指定します。
  • PROPERTY_HINT_RANGE と "0,20,0.01" を組み合わせると、Inspectorのフィールドがスライダーになります(最小値、最大値、刻み幅)。
  • ADD_SIGNAL(MethodInfo("name", PropertyInfo(...), ...)) — 型付きの引数を持つシグナルを宣言します。コードから emit_signal("position_changed", this, new_pos) で発行します。

⚠️ Godot 4.7での変更点: GDExtensionインターフェース関数の object_cast_to と classdb_get_class_tag は非推奨となり、is_class ベースのキャストが推奨されます。godot-cppなどのバインディングライブラリは内部で処理しますが、これらのインターフェース関数を直接呼び出すネイティブコードでは、キャスト処理を移行する必要があります。GH-119254 を参照してください。


4. エントリーポイントと .gdextension ファイル

エントリーポイント(register_types.cpp):

void 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();
}
}

.gdextension ファイル(project/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_symbol と完全に一致する必要があります。一致しないと、拡張機能は読み込まれません。クラスは MODULE_INITIALIZATION_LEVEL_SCENE の条件下で GDREGISTER_CLASS を使って登録します。[libraries] のキーは platform.feature.arch タグです。template_debug と template_release はビルド設定を区別します。省略可能なセクションは、ノードごとのエディターアイコンを指定する [icons] と、エクスポート時にコピーされる追加ライブラリを指定する [dependencies] です。

Godot 4.7 以降: GDExtension の生のインターフェースに、参照カウントに対応した構築および登録のエントリーポイントである classdb_construct_object3 と classdb_register_extension_class6 が追加されました(GH-118214)。4.7 ヘッダーに対してビルドされたバインディングライブラリは、これらを自動的に使用します。これらが関係するのは、インターフェースを直接呼び出すコードのみです。


5. 互換性ルール

前方互換ですが、後方互換ではありません。 4.2 を対象とする拡張機能は 4.3 で動作しますが、4.3 を対象とする拡張機能は 4.2 では読み込まれません。例外: 4.0 を対象とする拡張機能は、4.1 以降では動作しません。

  • godot-cpp ブランチは、ビルド対象のエンジンバージョンに合わせる必要があります。
  • reloadable = true によるホットリロードはエディター内のみで動作します。エクスポートしたゲームではリロードされません。
  • compatibility_minimum は必須です。実際にサポートする最も古いエンジンバージョンを設定してください。古いエンジンでは拡張機能の読み込みが拒否され、4.1 未満の値も拒否されます。
  • エクスポートしたゲームでは、対応する template_release バイナリが配置されていないと、ネイティブノード型は実行時に存在しません。

⚠️ Godot 4.7 での変更点: Object.is_class() の class パラメーターの型が、パフォーマンス向上のため String から StringName に変更されました。GDScript への影響はなく、C# には互換性メソッドが追加されましたが、古いシグネチャに対してコンパイルされた GDExtension バイナリは、エンジンの互換性マッピングに依存します。4.7 に更新する際は、対象バージョンに合ったバインディングに対して再ビルドしてください。詳しくは4.7 移行ガイドを参照してください。


6. GDScript と C# からの使用

ビルドして .gdextension ファイルを配置すると、ネイティブクラスは通常のノード型として表示されます。バインドされたプロパティはインスペクターに表示され(範囲ヒントはスライダーになります)、シグナルはノードドックに表示されます。

GDScript
extends 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])
C# 相当のコード
using Godot;

public partial class Demo : Node
{
    public override void _Ready()
    {
        // No C# type exists for a GDExtension class: create it by name.
        var node = ClassDB.Instantiate("GDExample").As<Node>();
        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}");
}

強い型付けの C# ラッパーを使うには、C# のグルークラスを同梱できます。ただし、拡張機能は上記の動的な Set/Connect/Call API でも完全に使用できます。

その他の言語とデバッグ: Rust(gdext) · ネイティブコードのデバッグ


実装チェックリスト

  • ネイティブ実装が本当に必要であることを確認した(プロファイリング済み。GDScript/C# では不十分。godot-optimizationを参照)
  • godot-cpp サブモジュールのブランチが対象エンジンバージョンに一致している
  • _bind_methods で公開するすべてのメソッド/プロパティ/シグナルをバインドしている
  • .gdextension の entry_symbol が、エクスポートされた extern "C" シンボルと一致している
  • compatibility_minimum にサポートする最も古いエンジンバージョンを設定している
  • [libraries] に、提供するすべてのプラットフォーム/アーキテクチャ向けの正しいパス(デバッグ用とリリース用)を設定している
  • リリース時のエクスポートに template_release バイナリが含まれている

コメント