godot 源码分析

学习godot源码,简单的介绍这个软件。

godot是一个开源的游戏引擎。非常小巧简单,对大型3d项目以外的游戏项目都非常适合。

我使用的godot是一个4.3左右的master版本。

编译运行

godot使用一个非常小众的编译工具,叫做 scons,相当于是一个 python版的 Makefile

scons非常的小巧,使用起来非常简单。开发基本都有python,直接pip就行。

pip install scons
scons

安装+运行,非常简单,自动多核编译,而且不需要任何的依赖。

一般玩一个代码第一步就是编译,这一步已经过去了。

随后打开 bin文件夹下的二进制文件,即可运行godot游戏编辑器。

简单的使用

godot基于场景节点构建整个游戏。使用起来非常简单,可以自行找教程,花不了多久。

编写完游戏后,会想要导出这个游戏,这个时候需要另外两个编译目标作为游戏模板。

scons target=template_debug
scons target=template_release

会在bin文件夹下生成两个二进制,顾名思义,是debug和release的模板。只要编译器支持,交叉编译也可以做到,换参数就行。

他们的运行原理是会把资源文件都打包成一个 .pck文件,template会想办法读取这个pck,等于是在虚拟机上跑脚本。

而我们之前编译的,其实是一个默认参数

scons target=editor

文件夹

源代码有一些文件夹,按字母顺序介绍一下

  • core是一些怎么着都会被编译的代码,重要且平台无关。
  • doc存放的文档,就是官网的文档,以xml格式组织,xml代表了节点的类和方法。
  • drivers是一些驱动,基本上是图形api和声音api。
  • editor包含了编辑器的代码,template就不会编译这个文件夹。
  • main是启动代码,也是平台无关的,主要就在这里区分editor和template,其他和 core定位差不多。
  • misc是一些乱七八糟的脚本,和代码没啥关系。
  • modules是模块,一些对第三方的简单封装,反正就是些模块。
  • platform是目标平台相关代码,一些平台api封装,举个例子就是syscall,fork这种,还有平台的图形api比如directx等等。
  • scene是场景节点代码,比如editor里用到的节点,Node3D这种,都在这里封装。
  • servers是一些功能封装,比modules高一点,比scenes低一点,不是真的服务器。
  • tests是测试。
  • thirdparty是第三方源码。

我们可以发现大概有一些封装的层次。

最高的是editor,他需要所有模块。

最低的是thirdparty,他不依赖任何文件,但是他不是真正属于godot的项目文件。其次是drivers,做的都是api接入的脏活累活,platform在他上面一点点。

调用的时候逻辑大概是:

editor->scene->servers->modules->core/platform->drivers->thirdparty

其他的忽略不计。

启动逻辑

main文件夹管理启动逻辑。

程序的启动的main在platform中,具体是哪个platform,取决于本机是那个platform,不然也跑不起来不是。比如我是gentoo,那就是linuxbsd。有时候是windows,就是windows。

经过platform的一通操作,会跑到两个 main的函数。

main的核心是一个 Main类,有 setupstart两个方法,顾名思义,setup初始化环境,start进入loop。这是基本的程序逻辑。

在这里,template和editor会有不同的逻辑。

  • template会在setup的时候读取pck文件,并进行解析验证,start会跳进脚本入口。
  • editor会在setup的时候做很少的事情,然后在start的时候跳进 editor文件夹下的 EditorNode类。

除了这两个最本质的区别,如果你的游戏很小,比如只有2d,可以通过宏裁剪template大小,也会通过宏影响到main是否初始化一些模块。

编辑器

editor包含了一个完整的编辑器,入口是 EditorNode节点。

用过qt和godot的同学会发现这俩玩意就是一模一样,或者说用c++的signal机制那就写出来只能一个样。

editor是一个非常传统的gui应用,使用了很多widget,存在 scene/gui文件夹下。

一些大的功能也放入了子文件夹,比如debugger可以连接新窗口的debug游戏,export可以打包成pck并连接到template,还有plugins读取编辑器插件。

编辑器包括了一些基本的widget,左边的treewidget,右边的属性编辑器,下面的资源管理器和通知和debug面板。

编辑器还在中间包括了三个复杂的编辑器,3dviewport,2dviewport,code editor,这三个编辑器代码量相对较多,特别是code editor,交互比较多,但是封装的比较好,可以把复杂度分摊到下面的scene,module,servers。

project manager封装了一些项目功能。

模板其实相对编辑器在c++的程度上简单,主要工作是读取pck。

场景节点

scene里提供了场景节点的功能。场景节点可以理解为godot提供的api和运行时。

一般游戏或者是应用,都跑在一个主线程上,godot也不例外,主线程主要功能就是管理node。main里可以看到加载根节点的功能,就是一个loop,也支持多个loop,我没有细看。

场景对于editor是通过c++的方式暴露接口,对于模板是通过脚本的方式暴露接口,相当于qt和pyside。

gui模块封装了一些gui模块,主要是editor会用,所以非常花哨,连节点编辑器都有。

2d模块是canvas。

3d模块比较复杂,最近render server在非常高强度的开发。

服务模块

serversmodules在相似的层级上封装了api。我还不是很了解内部原理。

modules是一些具体的功能,比如 fbxcsg等等。很多都是对第三方的模块的简单封装。

servers是一组功能,比如 renderingphysics_3d。组装了 modules的功能提供了人性化接口。比如很流行的渲染工具d3d12和vulkan都是在 rendering server里实现的。

重要功能

godot在代码上有一些核心的功能。

gdscript。这个是godot官方支持的脚本,gdscript和gdextension是两个平行的关系,他们可以控制节点,使用 _process这个godot核心函数。除了gdscript,C#的支持是最好的,因为有很多Unity用户很喜欢C#。

variant。这个其实是gdscript的类型系统,他用Callable等概念包装了gdscript的函数和类和基本类型。

signal。这个是godot的通信机制,脚本可以自定义signal发射,也可以处理其他节点的信号。

debug。godot的editor有个很强的debugger,可以运行游戏,而真的运行游戏是把资源和脚本打包成 pck文件,然后通过template运行,有点像浏览器跑js。