
摘要
godot-rust(代号 gdext)是 Godot 4 的 Rust 绑定,通过官方的 GDExtension 接口,让你用 Rust 写游戏逻辑、编辑器插件和自定义节点,不用改引擎源码,也不用重新编译 Godot。今年 3 月发布的 v0.5 带来了类型化字典、非空对象参数和随处可用的 Rust 枚举,之后的 0.5.x 补丁一路更新到 8 月的 v0.5.5,并加入了 Godot 4.7 的 API 支持,已经可以用在真实项目里。
背景与问题
Godot 的 GDScript 上手快,但游戏做大之后,常见的痛点是这几个:
- 寻路、物理、程序化生成这类热点代码,脚本语言跑不动
- 项目变大后,动态类型让重构变得提心吊胆
- 想复用 Rust 生态里现成的库,比如网络、序列化、ECS
以前的办法是改 C++ 模块,要自己编译整个引擎,维护成本很高。Godot 4 引入 GDExtension 之后,扩展被编译成独立的动态库,由编辑器在运行时加载,引擎本身不用动。
GDExtension 本身是一套 C API,直接写并不舒服。gdext 做的事,就是把它包成符合 Rust 习惯的类型安全接口。它是社区驱动的开源项目,许可证是 MPL-2.0,可以用于商业和闭源游戏,只有改动 godot-rust 本身时才需要公开修改。
注意区分:老的 gdnative 面向 Godot 3,已经不再积极维护,新项目直接用 gdext。
核心思路与优势
1. 类型安全,贴近 Rust 写法
用派生宏声明一个类,用属性宏暴露方法和信号,继承关系通过 Base<T> 表达。覆盖引擎虚函数时,实现对应的 trait 即可,比如 ISprite2D。
2. 和 GDScript 无缝互通
Rust 写的类注册后,在编辑器里和内置节点一样出现,可以直接拖进场景,GDScript 也能调用它的方法、连接它的信号。所以不必整个项目重写,只把热点部分换成 Rust。
3. 热重载
官方在持续集成里测试热重载,改完 Rust 代码重新编译,编辑器不必重启就能加载新库。这项能力从 Godot 4.2 开始才有,正好也是当前 gdext 的最低版本要求。
4. v0.5 这一轮的关键更新
下面是官方 v0.5 发布说明里集中介绍的内容,其中安全防护分档和自定义单例其实在 0.4.3、0.4.4 补丁里就已先行上线:
- 安全防护分三档:Strict(Debug 构建默认,尽可能多地抓错)、Balanced(Release 构建默认,快且安全)、Disengaged(牺牲安全换极致性能,出错即未定义行为,官方不建议轻易使用)
- 类型化字典
Dictionary<K, V>,新增dict!和idict!宏 - 去掉了
Gd<T>内部的互斥锁,对象访问更快;Callable::from_fn()也做了明显优化 - 引擎 API 中标注为非空的对象参数和返回值直接用
Gd<T>,不再是Option<Gd<T>> - Rust 枚举可以用在
#[func]、#[signal]和#[var]上 - 用
#[class(singleton)]定义自己的单例 - 一个 Rust 扩展可以作为 rlib 依赖另一个 Rust 扩展
- 对齐 Godot 4.6 的 API,并支持 Rust 2024 Edition
- 破坏性变更:
#[var]生成的 getter/setter 默认不再能在 Rust 里直接调用,需要写成#[var(pub)]
v0.5 之后的补丁同样值得关注:v0.5.4(6 月)新增 api-4-7 特性以支持 Godot 4.7,v0.5.5(8 月)让 #[func(virtual)] 支持 async fn,并缓存了单例、降低了引用计数开销。
5. 版本兼容规则清晰
扩展可以被任意 Godot 版本加载,前提是运行时版本不低于编译时选定的 API 版本。用 4.2 编译的扩展能在 4.2.1 和 4.3 上跑,用 4.3 编译的则不能在 4.2.1 上跑。所以想兼容更多用户,就选较低的 API 版本编译。gdext 0.4 及之后的版本,最低要求 Godot 4.2。
面向人群
- 已经在用 Godot,但被 GDScript 性能卡住的开发者
- 熟悉 Rust,想用它做 2D、3D 游戏,又不想从零搭引擎的人
- 想做编辑器插件、自动化工具或程序化生成器的开发者
- 看过 Bevy 与 Godot 的路线之争,想要"Godot 成熟工具链加 Rust 类型系统"折中方案的人
需要提前知道的是:目前 Linux、Windows、macOS 这三个桌面平台是主力支持的,Wasm(Web 导出)、Android、iOS 还属于实验性,文档和工具链都不完善。以 Web 导出为例,目前还没有预构建产物,需要 nightly Rust、Emscripten 和自定义 API 配置,门槛不低。项目也会不定期引入破坏性变更,通常改动不大,并附有迁移指南。
实践步骤
下面走一遍最小可运行的流程。
第一步:创建 Rust 库
在 Godot 项目旁边新建一个 Rust 工程,Cargo.toml 里把库类型设为 cdylib,并添加 godot 依赖:
[package]
name = "rust_project"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
godot = "0.5"
cdylib 表示编译成 C 风格的动态库,Godot 才能加载它。
第二步:写入口
src/lib.rs 里只需要注册一个扩展入口:
use godot::prelude::*;
struct MyExtension;
#[gdextension]
unsafe impl ExtensionLibrary for MyExtension {}
第三步:写一个自定义节点
下面这个 Player 继承自 Sprite2D,每个物理帧旋转一点,并提供一个能加速的方法和一个信号:
use godot::prelude::*;
use godot::classes::{ISprite2D, Sprite2D};
#[derive(GodotClass)]
#[class(base=Sprite2D)]
struct Player {
speed: f64,
angular_speed: f64,
base: Base<Sprite2D>,
}
#[godot_api]
impl ISprite2D for Player {
fn init(base: Base<Sprite2D>) -> Self {
Self {
speed: 400.0,
angular_speed: std::f64::consts::PI,
base,
}
}
fn physics_process(&mut self, delta: f64) {
let radians = (self.angular_speed * delta) as f32;
self.base_mut().rotate(radians);
}
}
#[godot_api]
impl Player {
#[func]
fn increase_speed(&mut self, amount: f64) {
self.speed += amount;
self.signals().speed_increased().emit();
}
#[signal]
fn speed_increased();
}
几个要点:
#[derive(GodotClass)]加#[class(base=Sprite2D)]声明这是一个 Godot 类及其基类- 字段
base: Base<Sprite2D>是访问基类的入口,通过base_mut()调用父类方法 impl ISprite2D是覆盖引擎虚函数的地方,这里实现了init和physics_process#[func]把方法暴露给 GDScript 和编辑器,#[signal]声明信号,用self.signals()发射
第四步:写 .gdextension 文件
在 Godot 项目里新建一个 .gdextension 文件,告诉引擎去哪里找动态库:
[configuration]
entry_symbol = "gdext_rust_init"
compatibility_minimum = 4.2
reloadable = true
[libraries]
linux.debug.x86_64 = "res://../rust/target/debug/librust_project.so"
linux.release.x86_64 = "res://../rust/target/release/librust_project.so"
windows.debug.x86_64 = "res://../rust/target/debug/rust_project.dll"
windows.release.x86_64 = "res://../rust/target/release/rust_project.dll"
macos.debug = "res://../rust/target/debug/librust_project.dylib"
macos.release = "res://../rust/target/release/librust_project.dylib"
库名要和 Cargo.toml 里的包名一致。reloadable = true 打开热重载,路径按你的目录结构调整。
第五步:编译并在编辑器里使用
cargo build
编译完成后打开 Godot 项目,在"创建节点"对话框里就能搜到 Player,和内置节点用法完全一样。以后每次修改 Rust 代码,重新执行 cargo build,切回编辑器窗口时它会自动重新加载扩展。
什么时候该用,什么时候不必
适合用 Rust 的部分:寻路、碰撞、程序化地图、大量单位的逻辑更新、需要长期维护的核心系统。
继续用 GDScript 的部分:UI 逻辑、关卡脚本、一次性的小玩法、需要快速试错的原型。
比较务实的做法是混合使用:GDScript 负责胶水和内容迭代,Rust 负责性能和稳定性要求高的核心。两者可以在同一个项目里自由互调,不必二选一。
我的看法
在 Godot 和 Bevy 的选择里,godot-rust 给出了第三条路:保留 Godot 的编辑器、场景系统和导出流程,同时把最吃性能、最需要类型约束的部分交给 Rust。
不过也要认清现状:它还没有到 1.0,API 会继续调整,移动端和 Web 端还在完善。如果你的项目主要面向桌面平台,并且团队愿意投入学习 Rust,现在就是很好的时机。如果目标是尽快上手做原型,先用 GDScript,等真正遇到性能瓶颈再把那一块换成 Rust 也不迟。
项目地址
github.com/godot-rust/gdext
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/yaoge1234/article/details/166571463




