为什么我们启动GDCC项目
你心里可能会有疑惑,Godot引擎不是自带GDScript解释器吗?为什么我们还需要一个编译器来编译脚本呢? 实际上,编译脚本这件事情,在游戏业界其实是一件很经常的事情,比如虚幻引擎的蓝图、Unity引擎的IL2CPP方案等。
为什么要编译脚本
我们之所以需要编译脚本,是因为有着迫切的需求:
- 我们需要避免游戏被类似于gdsdecomp这样的软件一键解包得到源代码,以避免被竞争对手光速洗稿
- 我们有一些游戏逻辑需要避免被轻易挖掘
- 我们需要提升脚本的运行速度,避免CPU瓶颈
- 我们需要在一些严格禁止动态逻辑的平台上分发游戏
- 我们希望去除GDScript解释器以获取更小的游戏包体
- ……
而这一切,都需要一个完善的现代编译器,来支持Godot社区的语言现代化,支撑起这些需求和背后的应用场景。
回顾过去:GDScript编译的来时路
制作一个GDScript编译器并非新想法,实际上,社区为了这件事情已经等了整整十年了:
-
2016.06:JIT 讨论进入 Godot issue
Godot issue
#5049提出为 GDScript 引入 JIT,并讨论 LLVM IR、DynASM/LuaJIT 以及把 GDScript 转成 C++ 等方向。早期讨论很快触及一个核心问题:JIT 在不同平台上的可用性并不一致,而且动态语言要跑得快,往往还需要可靠的类型推断。 -
2017.09:导出时编译为 GDNative/C 的想法出现
issue
#11068提出在导出游戏时把 GDScript 编译成 GDNative C 代码,从而避免运行时解释执行。讨论焦点集中在:是否需要随 Godot 带一个 C/C++ 编译器、LLVM 是否太重、跨平台导出工具链如何处理,以及静态类型能否帮助生成更高效的代码。 -
2018.03:社区尝试 GDScript 到 GDNative C++
社区项目
gds2gdn开始尝试把 GDScript 转成godot-cpp兼容的 GDNative C++ 代码,并生成对应的.gdns文件。项目 README 当时也明确说明它仍处于早期阶段,主要还在解析 GDScript、输出语法树,距离完整可用的 C++ 输出还有不少限制。 -
2018.07:Godot 3.1 引入 GDScript 可选类型
Godot 官方博客介绍了 GDScript 的 type hints。它首先是为了更好的错误检查、补全和可读性,但文末也把它写成后续性能优化的基础:未来可以为已知类型的代码加入更快的指令,并在编译阶段做更多优化。
-
2019.03:Godot 3.1 发布,性能路线转向 typed instructions
Godot 3.1 发布文章说明,可选类型在 3.1 中仍主要是解析和检查层面的功能;后续计划是在 GDScript 运行时中加入 typed instructions,让有类型信息的代码执行得更快。这是路线从“直接做 JIT”转向“先利用类型信息优化解释器”的重要节点。
参考:Godot 3.1 is out。
-
2019.09:官方公开提到 typed instructions 和未来编译到 C
Juan Linietsky 在宣布 George Marques 全职参与 Godot 的文章中写到,George 的 GDScript 工作计划包括 typed instructions,并提到最终可能把脚本编译到 C。后来社区常把这段看作 GDScript 性能路线的早期公开承诺来源之一。
参考:George Marques will be working full time for the project。
-
2019-2020:社区 AOT 和解释器优化实验
pchasco 在讨论中提到自己做过较多 GDScript 到 C/GDNative 的尝试,但实践中遇到 GDNative 调用层开销、旧 bytecode 难以稳定复用等问题,因此转向更贴近现有运行时的优化实验,例如控制流分析、删除无用代码、跳转整理等。
参考:pchasco 对 AOT 尝试的总结、optimizer 分支说明、pchasco/godot optimizer 目录。
-
2020.05:GDScript 2.0 重写公开开始
George Marques 在 GDScript 进展报告中说明,他正在重写 GDScript 实现,让整体结构更接近常见语言实现方式,便于维护、错误报告和后续优化。第一篇报告从 tokenizer 开始,也说明了这次重写并不是只改语法,而是在重整整个处理流程。
-
2020.06:新 parser、annotations 和 await 进入 GDScript 2.0
第二篇进展报告介绍了新 parser、多错误报告、annotations、
await替代yield等变化。这些改动奠定了 GDScript 2.0 的语言表面,也让之后的类型检查和优化更容易接入。 -
2020.06:官方基本放弃 JIT 作为优先方向
vnen 在关闭 JIT 旧 issue 时表示,加入 JIT 非常不可能,相关想法应迁移到新的 proposal 流程;后续讨论也指出 iOS、Web 和部分主机平台不支持 JIT。这个节点之后,官方讨论明显更偏向 AOT、离线编译或继续优化现有运行时。
-
2020.07:类型检查、警告和常量计算回归
第三篇进展报告说明,新的类型检查被移到独立分析阶段。GDScript 不强迫所有变量都写类型,但会尽量推断表达式和变量类型,这既能提前发现更多错误,也能为后续性能优化提供更可靠的信息。
-
2020.08:GDScript 2.0 合并到 master,并加入 codegen abstraction 计划
第四篇进展报告宣布新的 GDScript 代码已经合并到 Godot
master。文章还提到正在做 code generation interface abstraction:当时目标仍是 GDScript VM,但这个结构可以为未来更换输出目标、加入优化阶段铺路。参考:GDScript progress report: New GDScript is now merged、godot#40598。
-
2020.10:typed instructions 落地,兑现一部分性能承诺
GDScript 2.0 加入 typed instructions:当类型在处理代码时已经明确,运行时可以跳过部分动态查找,直接走更快路径。官方报告给出的合成测试显示,不同操作大约有 5% 到 150% 不等的提升。但这仍是解释器内的优化,不是把 GDScript 编译成原生二进制。
参考:GDScript progress report: Typed instructions、godot#43004。
-
2021.06:GDScript 2.0 对 Godot 4.0 feature-complete
第六篇进展报告宣布 GDScript 对 Godot 4.0 来说已经 feature-complete,包含 typed arrays、lambda、内建类型 static methods、减少寻址模式、typed temporaries stack 和测试套件等。报告仍提到还计划继续优化运行时性能,但原生 AOT/JIT 没有进入 Godot 4.0。
-
2021.07:社区提出 GDPP / GDScript++
proposal
#3069提出 GDPP / GDScript++:一种受 GDScript 2.0 启发、输出到 C++ 和 GDExtension 的语言。它不是 Godot 官方核心功能,而是社区对“保留 GDScript 风格,同时获得 C++/GDExtension 性能”的一次探索。 -
2021.09:WebAssembly runtime 方案被提出
proposal
#3370提议把 GDScript AST 编译成 WebAssembly bytecode,再通过 WASM runtime 以解释、JIT 或 AOT 的方式运行。它的吸引力在于跨平台和复用现有 WASM 技术,但截至目前仍是开放 proposal,没有进入 Godot 主线。 -
2023.03:Godot 4.0 发布,GDScript 2.0 正式落地
Godot 4.0 发布文章总结了 GDScript 的新能力:更强的静态类型、typed arrays、lambda、first-class signals、新 property 语法、
await和super、更好的错误报告等。文章也说运行时更快更稳定,但这来自语言后端重写和 VM 优化,而不是原生 AOT/JIT。 -
2023.01 至今:官方重新集中讨论 VM 性能、AOT 和离线编译
Juan Linietsky 在 proposal
#6031中总结:GDScript 2.0 已经让 VM 拿到更多类型信息、函数指针和更好的代码生成结构,但由于 parser/compiler 重写工作量很大,VM 优化本身没有充分展开。该 proposal 比较 JIT、实时 AOT 和离线编译,并指出理想方向可能是导出时把脚本编译成随游戏一起分发的共享库,再通过 GDExtension 访问引擎 API。 -
2023.12 至今:GDScript 中间格式和二进制导出讨论
proposal
#8605提出在导出时保存更接近已处理代码的 GDScript 中间格式,减少加载时重复解析、避免直接分发.gd源码,并允许导出模板不包含完整 GDScript 编译器。它不是原生 AOT;提案中也说明,如果未来真正把 GDScript 编译成机器码,这个格式可能会被取代。 -
2024.02:Godot 4.3 重新引入 GDScript binary token export
PR
#87634为 Godot 4.x 重新加入导出 GDScript 二进制 token 的能力,并默认启用。这个功能能改善加载和源码可读性问题,但它本质上仍是 tokenized source 级别的格式,不是把 GDScript 编译成原生代码。参考:godot#87634。
-
2024.02:社区项目 GdScript2All
GdScript2All创建,目标是把 Godot 4 GDScript 转换到 C# 和 C++,并提供编辑器插件和命令行工具。README 也明确列出生成代码可能需要人工修正、pattern matching 不支持、lambda 和部分 C++ 输出不完美等限制。
展望未来:GDCC的目标
我们希望拥有一个强大的GDScript编译器,这就是我们启动GDCC项目的初衷,我们想要一个:
- 易于迁移的编译器,几乎无需修改GDScript脚本代码就能直接编译为本机二进制库
- 动态类型与静态类型结合的编译器,允许我们享受动态类型的便利同时也享受静态类型的高效
- 安全的编译器,如果需要的话几乎不可能从编译后的产物中恢复源代码
- 高性能的编译器,编译后的代码可以有更高的性能,完全静态类型的部分可以媲美手写的C++代码
- 快速的编译器,可以在极短的时间内完成整个项目的编译,无需长久的等待
同时,我们想要的不仅仅是一个编译器,更是一个GDScript开发的全流程工具链,让GDScript真正成为游戏开发的瑞士军刀,我们还想要:
- 高效的代码格式化器,让代码永远保持优雅
- 快速的LSP实现,可以在不启动引擎的情况下就提供基本的代码辅助与诊断
- 智能的面向AI的工具,让编程智能体更快速准确地编写、调试、诊断GDScript代码
- ……
千里之行,始于足下,愿此行,终抵群星。