分类:Godot 引擎
学习路径:由浅入深,按顺序阅读效果最好(共 149 篇)
-
1. Godot 引擎底层原理导览:一张给源码读者的地图
给想翻 Godot 源码的人的一张地图:节点层与服务器层的二分结构、主循环节拍、脚本运行时位置,以及这个分类下每篇深度文章的阅读路径。
-
2. Object 基类与 ClassDB:Godot 反射系统与信号槽的地基
C++ 没有反射,Godot 用 GDCLASS 宏和 ClassDB 静态注册表造了一套。这篇讲类怎么注册、方法怎么绑定、信号连接存在哪,以及 emit_signal 为什么要在发射前复制一份连接列表。
-
3. Godot 4.x 源码目录速览:main/core/scene/servers/modules/editor 各管什么
第一次 clone Godot 源码的人都会被 40 多个顶层目录劝退。这篇把 4.x 的目录按"分层职责"重新组织成一张地图,并标出每层最值得先读的文件。
-
4. Main 类启动流程拆解:从 main() 到 MainLoop 开始迭代
main/main.cpp 四千多行,但真正决定"引擎怎么活过来"的只有 setup、setup2、start 三段。这篇把启动序列按依赖关系理一遍,标出每步为什么必须在这个位置。
-
5. Variant 的内部结构:union 加类型标签怎么装下四十多种类型
Variant 是脚本与 C++ 之间的通用货币,要在固定 24 字节里装下从 int 到 Transform3D 再到 Object 指针的 40 多种类型。这篇拆 core/variant/variant.h 的内存布局,讲清 4 字节类型标签加 16 字节 union 的取舍。
-
6. RefCounted 引用计数与 memnew/memdelete:引擎为什么不用 shared_ptr
Godot 的资源生命周期靠 RefCounted + Ref<T> 管理,内存分配靠 memnew/memdelete 宏。这篇讲这套组合为什么故意绕开 std::shared_ptr,以及引用计数在继承体系里是怎么实现的。
-
7. RID 句柄系统:服务器对象为什么不用裸指针
RenderingServer、PhysicsServer 对外暴露的不是对象指针而是 RID——一个 64 位整数句柄。这篇讲 RID 的查表实现、为什么它能带来线程安全和热重载能力,以及 RID_Owner 的槽位复用机制。
-
8. ERR_FAIL_COND 家族:引擎错误处理宏的设计逻辑
Godot 源码里看不到一个 try/catch,错误处理全靠 ERR_FAIL_COND、ERR_CONTINUE、CRASH_NOW 这三十多个宏。这篇讲这套宏的设计动机、六个变体的分工,以及"不用异常"换来的东西。
-
9. core/os 层:OS 单例如何抽象平台差异
Godot 跨六个桌面/移动/Web 平台,平台差异被压在 core/os 的 OS 基类和 platform/ 的派生实现里。这篇讲 OS::get_singleton() 的分发机制、虚函数表的覆盖策略,以及为什么"单例 + 继承"比"接口 + 工厂"更适合引擎。
-
10. SCons 构建系统:自定义编译选项与模块开关
Godot 用 SCons 而不是 CMake,不是因为历史惯性——SConstruct 里那套"选项对象 + 平台 SCsub + 模块开关"的组合,是裁剪引擎体积和交叉编译的关键。这篇讲构建系统的分层结构和最常用的定制手法。
-
11. 模块注册机制:register_module_types 如何串起所有模块
Godot 的 40 多个模块(GDScript、glTF、Jolt、mbedtls……)通过一套生成代码机制注册到引擎里。这篇讲 register_module_types.gen.cpp 的生成逻辑、三阶段初始化的分工,以及静态初始化顺序问题的规避方案。
-
12. GDExtension 与 godot-cpp:ABI 兼容层是怎么设计的
GDExtension 让 C++ 代码编译成动态库热插进引擎,且引擎升级后不用重编译。这篇讲 GDExtensionInterface 函数表的设计、版本协商机制,以及 godot-cpp 绑定层如何把 C 接口包装成 idiomatic C++。
-
13. String/StringName 的内部表示与哈希缓存
StringName 用一张全局静态表把字符串比较变成指针比较,这篇拆 core/string/string_name.h 的结构,讲清为什么 NodePath 与信号名的比较必须走 StringName 而不是 String。
-
14. Vector/HashMap/LocalVector:Godot 为什么不用 STL
core/templates 下有一套自研容器,这篇对比 Vector、LocalVector、HashMap 与 STL 对应物的设计分歧,讲清拷贝语义、内存模型和 ABI 稳定性这三条不用 STL 的理由。
-
15. Callable 与信号的关系:bind 的参数存在哪
Callable 是"可调用的东西"的统一表示,bind 的参数不是存在信号里而是存在 Callable 自己内部。这篇读 core/variant/callable.h 与 callable_bind.cpp,讲清信号发射时参数怎么拼接。
-
16. 命令行解析与启动参数:--editor、--headless 的分发逻辑
godot --editor --headless 这些参数在 Main::setup() 里被一棵参数树消化,这篇读 main/main.cpp 的参数解析段,讲清参数怎么决定显示驱动、脚本服务器和 MainLoop 的选择。
-
17. doctest 单元测试与 clang-format 工作流:给引擎提 PR 前的自检清单
Godot 的测试跑在引擎二进制里而不是独立测试程序,这篇讲 tests/ 目录的 doctest 集成方式、headless 跑法,以及 clang-format 在 CI 上的卡点。
-
18. ObjectDB 与对象泄漏检测:退出时那堆 "ObjectDB instances leaked" 怎么读
游戏退出时打印的 "ObjectDB instances leaked at exit" 不是报错是遗产清单,这篇讲 ObjectDB 的注册机制、泄漏报告的读法,以及它和 Orphaned Nodes 的区别。
-
19. 多线程基础设施:WorkerThreadPool 与 TaskGroup
WorkerThreadPool 是引擎内部所有"扔给后台干"的活儿的统一入口,这篇讲它的任务队列、Group 语义,以及为什么引擎不用 std::async 也不让业务自己起线程。
-
20. Mutex/RWLock 封装与服务器线程模式:thread_safe 编译宏的影响
Godot 的锁不是 std::mutex 的直接包装,而是一套带"可编译期消除"语义的封装,这篇讲 Mutex/RWLock 的分层、THREAD_SAFE 宏如何让锁在单线程构建里消失,以及服务器的线程模式设置。