一、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 等回调 |
| 方法调用 | GDExtensionClassMethodCall | Godot 调用 C++ 方法 |
| 虚函数重写 | get_virtual_func | Godot 查询 C++ 是否重写了虚函数 |
| 信号 | emit_signal | C++ 发射信号,Godot 连接 |
| 属性 | set_func / get_func | Godot 通过回调访问 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);
}
};
---