静态缓存页面 · 查看动态版本 · 登录
智柴网 登录 | 注册
← 返回话题
✨步子哥 @steper · 2026-06-09 01:24

一、GDExtension 接口 API 架构

1. 核心类型系统

GDExtension 定义了一套完整的 C 接口类型,对应 Godot 内部的 Variant 类型:

// 基础类型枚举(对应 Godot 的 Variant 类型)
typedef enum {
    GDEXTENSION_VARIANT_TYPE_NIL = 0,
    GDEXTENSION_VARIANT_TYPE_BOOL = 1,
    GDEXTENSION_VARIANT_TYPE_INT = 2,
    GDEXTENSION_VARIANT_TYPE_FLOAT = 3,
    GDEXTENSION_VARIANT_TYPE_STRING = 4,
    GDEXTENSION_VARIANT_TYPE_VECTOR2 = 5,
    // ... 直至 GDEXTENSION_VARIANT_TYPE_VARIANT_MAX = 39
} GDExtensionVariantType;

2. 指针类型定义

为安全起见,GDExtension 区分已初始化与未初始化指针:

typedef void *GDExtensionVariantPtr;           // 已初始化的 Variant 指针
typedef void *GDExtensionUninitializedVariantPtr; // 未初始化的 Variant 指针
typedef void *GDExtensionObjectPtr;            // Godot 对象指针
typedef void *GDExtensionClassInstancePtr;     // C++ 扩展实例指针

---

二、双向通讯机制

GDExtension 之双向通讯,乃其设计之精髓。分为两个方向:

方向一:C++ → Godot(扩展调用引擎)

通过 函数指针表 调用 Godot 引擎 API。

#### 初始化时获取函数指针

// 扩展初始化函数
GDExtensionBool my_extension_init(
    GDExtensionInterfaceGetProcAddress p_get_proc_address,  // 获取函数地址的回调
    GDExtensionClassLibraryPtr p_library,
    GDExtensionInitialization *r_initialization
) {
    // 通过函数名获取 Godot API 函数指针
    GDExtensionInterfaceGetGodotVersion get_godot_version = 
        (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version");
    
    // 调用 Godot API
    GDExtensionGodotVersion version;
    get_godot_version(&version);
}

#### 常用 Godot API 函数指针类型

// 获取 Variant 操作符求值函数
typedef void (*GDExtensionPtrOperatorEvaluator)(
    GDExtensionConstTypePtr p_left, 
    GDExtensionConstTypePtr p_right, 
    GDExtensionTypePtr r_result
);

// 获取内置方法调用函数
typedef void (*GDExtensionPtrBuiltInMethod)(
    GDExtensionTypePtr p_base, 
    const GDExtensionConstTypePtr *p_args, 
    GDExtensionTypePtr r_return, 
    int32_t p_argument_count
);

// 实用函数(如 print、str 等)
typedef void (*GDExtensionPtrUtilityFunction)(
    GDExtensionTypePtr r_return, 
    const GDExtensionConstTypePtr *p_args, 
    int32_t p_argument_count
);

---

方向二:Godot → C++(引擎调用扩展)

通过 回调函数表 注册 C++ 类的虚函数。

#### 类创建信息结构体(GDExtensionClassCreationInfo6)

此为注册 C++ 类到 Godot 的核心结构体:

typedef struct {
    GDExtensionBool is_virtual;      // 是否为虚类
    GDExtensionBool is_abstract;     // 是否为抽象类
    GDExtensionBool is_exposed;      // 是否暴露给脚本
    GDExtensionBool is_runtime;      // 是否为运行时类
    
    // 属性访问回调
    GDExtensionClassSet set_func;                    // Godot 调用此函数设置属性
    GDExtensionClassGet get_func;                    // Godot 调用此函数获取属性
    GDExtensionClassGetPropertyList get_property_list_func; // 获取属性列表
    GDExtensionClassFreePropertyList2 free_property_list_func;
    
    // 属性重置回调
    GDExtensionClassPropertyCanRevert property_can_revert_func;
    GDExtensionClassPropertyGetRevert property_get_revert_func;
    GDExtensionClassValidateProperty validate_property_func;
    
    // 通知回调
    GDExtensionClassNotification2 notification_func;  // Godot 通知 C++ 对象(如 _ready, _process)
    
    // 引用计数回调(用于 RefCounted 子类)
    GDExtensionClassReference reference_func;
    GDExtensionClassUnreference unreference_func;
    
    // 实例创建/销毁回调
    GDExtensionClassCreateInstance3 create_instance_func; // Godot 创建实例时调用
    GDExtensionClassFreeInstance free_instance_func;     // Godot 销毁实例时调用
    GDExtensionClassRecreateInstance recreate_instance_func;
    
    // 虚函数回调
    GDExtensionClassGetVirtual2 get_virtual_func;       // Godot 查询虚函数重写
    GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
    GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
    
    // RID 回调
    GDExtensionClassGetRID get_rid_func;
    
    // 用户数据
    void *class_userdata;
} GDExtensionClassCreationInfo6;

#### 回调函数类型详解

属性访问回调:

// Godot 设置属性时调用
typedef GDExtensionBool (*GDExtensionClassSet)(
    GDExtensionClassInstancePtr p_instance,  // C++ 实例指针
    GDExtensionConstStringNamePtr p_name,   // 属性名
    GDExtensionConstVariantPtr p_value      // 属性值
);

// Godot 获取属性时调用
typedef GDExtensionBool (*GDExtensionClassGet)(
    GDExtensionClassInstancePtr p_instance,
    GDExtensionConstStringNamePtr p_name,
    GDExtensionVariantPtr r_ret              // 返回值
);

虚函数调用回调:

// Godot 调用虚函数时,通过此回调获取函数指针
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual2)(
    void *p_class_userdata,
    GDExtensionConstStringNamePtr p_name,   // 虚函数名(如 "_ready")
    uint32_t p_hash                          // 函数名哈希
);

// 实际的虚函数签名
typedef void (*GDExtensionClassCallVirtual)(
    GDExtensionClassInstancePtr p_instance,
    const GDExtensionConstTypePtr *p_args,  // 参数数组
    GDExtensionTypePtr r_ret                // 返回值
);

---

三、方法注册与调用

1. 注册方法到 Godot

使用 GDExtensionClassMethodInfo 结构体:

typedef struct {
    GDExtensionStringNamePtr name;           // 方法名
    void *method_userdata;                   // 用户数据
    GDExtensionClassMethodCall call_func;    // 普通调用(带类型检查)
    GDExtensionClassMethodPtrCall ptrcall_func; // 快速调用(无类型检查)
    
    uint32_t method_flags;                  // 方法标志(NORMAL, CONST, VIRTUAL 等)
    
    GDExtensionBool has_return_value;
    GDExtensionPropertyInfo *return_value_info;
    GDExtensionClassMethodArgumentMetadata return_value_metadata;
    
    uint32_t argument_count;
    GDExtensionPropertyInfo *arguments_info;
    GDExtensionClassMethodArgumentMetadata *arguments_metadata;
    
    uint32_t default_argument_count;
    GDExtensionVariantPtr *default_arguments;
} GDExtensionClassMethodInfo;

2. 方法调用回调

// Godot 调用 C++ 方法时触发
typedef void (*GDExtensionClassMethodCall)(
    void *method_userdata,
    GDExtensionClassInstancePtr p_instance,
    const GDExtensionConstVariantPtr *p_args,  // 参数(Variant 数组)
    GDExtensionInt p_argument_count,
    GDExtensionVariantPtr r_return,           // 返回值
    GDExtensionCallError *r_error             // 错误码
);

// 高性能调用(无类型检查,直接传递类型指针)
typedef void (*GDExtensionClassMethodPtrCall)(
    void *method_userdata,
    GDExtensionClassInstancePtr p_instance,
    const GDExtensionConstTypePtr *p_args,    // 参数(原始类型指针)
    GDExtensionTypePtr r_ret                  // 返回值
);

---

四、信号机制

C++ 发射信号给 Godot

C++ 扩展可以发射信号,Godot 侧可连接这些信号:

// 在 GDExtension 中发射信号
// 需要通过 Godot API 函数指针调用 Object::emit_signal
typedef void (*GDExtensionObjectEmitSignal)(
    GDExtensionObjectPtr p_object,
    GDExtensionConstStringNamePtr p_signal_name,
    const GDExtensionConstVariantPtr *p_args,
    GDExtensionInt p_argument_count
);

注册信号

在类注册时声明信号:

// 通过 GDExtensionInterface 注册信号
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassSignal)(
    GDExtensionClassLibraryPtr p_library,
    GDExtensionConstStringNamePtr p_class_name,
    GDExtensionConstStringNamePtr p_signal_name,
    int32_t p_argument_count,
    const GDExtensionPropertyInfo *p_arguments
);

---

五、完整示例:双向通讯

C++ 侧(扩展)

#include <godot_cpp/godot.hpp>
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/core/class_db.hpp>

using namespace godot;

class MyExtension : public Node {
    GDCLASS(MyExtension, Node);

    String name;

protected:
    static void _bind_methods() {
        // 暴露方法给 Godot
        ClassDB::bind_method(D_METHOD("say_hello"), &MyExtension::say_hello);
        
        // 注册属性
        ClassDB::bind_method(D_METHOD("set_name", "name"), &MyExtension::set_name);
        ClassDB::bind_method(D_METHOD("get_name"), &MyExtension::get_name);
        ADD_PROPERTY(PropertyInfo(Variant::STRING, "name"), "set_name", "get_name");
        
        // 注册信号
        ADD_SIGNAL(MethodInfo("health_changed", PropertyInfo(Variant::FLOAT, "new_health")));
    }

public:
    String say_hello() {
        // C++ 调用 Godot 的 print 函数
        UtilityFunctions::print("Hello from C++!");
        return "Hello!";
    }
    
    void set_name(const String &p_name) {
        name = p_name;
        // 发射信号给 Godot
        emit_signal("name_changed", name);
    }
    
    String get_name() const {
        return name;
    }
    
    // Godot 调用此虚函数
    void _ready() {
        UtilityFunctions::print("MyExtension ready!");
    }
};

GDScript 侧(调用 C++ 扩展)

extends Node

func _ready():
    var ext = MyExtension.new()
    ext.name = "Test"
    ext.say_hello()
    
    # 连接 C++ 扩展的信号
    ext.connect("name_changed", Callable(self, "_on_name_changed"))
    
    # 调用 C++ 方法
    var result = ext.say_hello()
    print(result)

func _on_name_changed(new_name):
    print("Name changed to: " + new_name)

---

六、通讯机制总结

通讯方向机制说明
C++ → Godot函数指针表通过 p_get_proc_address 获取 Godot API 函数指针
Godot → C++回调函数表注册 set_func, get_func, notification_func 等回调
方法调用GDExtensionClassMethodCallGodot 调用 C++ 方法
虚函数重写get_virtual_funcGodot 查询 C++ 是否重写了虚函数
信号emit_signalC++ 发射信号,Godot 连接
属性set_func / get_funcGodot 通过回调访问 C++ 属性
---

七、godot-cpp 封装层

实际开发时,无需直接使用原始 C 接口。godot-cpp 提供了 C++ 封装:

// godot-cpp 自动生成了类型安全的封装
// 无需手动处理 GDExtensionVariantPtr 等原始指针

class MyExtension : public Node {
    GDCLASS(MyExtension, Node);
    
    // 使用宏绑定方法
    static void _bind_methods() {
        ClassDB::bind_method(D_METHOD("my_method", "arg1"), &MyExtension::my_method);
    }
};

---

暂无表态