Godot-GDExtension C++ 环境搭建 (Docker+MinGW/跨平台)

Godot 在4.X 之后推出了 GDExtension,通过第三方绑定扩展功能,目前官方支持的语言只有 C++。通过使用 GDExtension C++ 编写扩展插件,可以作为库文件在 Godot 中交互使用。GDExtension 可以使用 C++ 原生库,提高了性能,还可以自编写游戏逻辑,无需和引擎一同编译。

背景与介绍

笔者刚开始学习 Godot 游戏开发,发现关于 GDExtension 的介绍资料较少,官方文档也很简略,导致我在一开始的环境配置时就踩了不少坑。我主要在 Windows 环境下进行 Godot 开发,Windows 平台下的 C++ 环境可以通过 Microsoft Visual Studio(cl) 或者 MinGW(gcc) 两种方式来进行配置,考虑到 Visual Studio 本身体积庞大,我最后选择了 MinGW-w64 作为 C++ 的编译环境。

一开始我尝试着通过 msysy2 安装 MinGW,但是问题层出不穷,因为平时笔者平时编码都是在 Linux 操作系统,Windows 下的环境配置并不算熟悉,之后还要考虑动态库链接和 Python 环境等问题,于是我在 Github 上找了一圈,看见有人通过 Docker 配置编译环境,只需要本地电脑安装 Git bashDocker 即可,这种方式简单快捷,并且解耦了环境配置与实际开发环境,以后我即使换电脑,甚至换操作系统,也可以很快搭建成功工作流,开箱即用。我在该方案上做了些许调整,接下来介绍一下具体的配置过程。

代码仓库地址https://github.com/convexwf/godot-gdextension-cpp-examples

打包镜像地址convexwf/godot-gdextension-cpp-builder

宿主机环境

  • OS: Windows 10
  • Docker Desktop 4.29.0
  • Git version 2.37.3
  • Godot 4.2.2 + godot-cpp 4.2

镜像环境

  • Python 3.11.9
  • scons 4.5.2
  • MinGW-w64 10.2.1(gcc-10)

环境和工具介绍

MinGW 的全称是 Minimalist GNU for Windows,本质是将包括 gcc 在内的 GNU 工具链移植到 Windows 平台,可以将源代码编译成为 Windows 平台下的后缀为 .exe 的可执行文件。MinGW 也可以在 Linux 平台下使用。

MinGW 的编译目标仅兼容 32 位应用程序,于是衍生出了 MinGW-w64,支持 32 位和 64 位的应用程序。MinGW-w64 下载链接见 Downloads - MinGW-w64

Scons 是一个基于 Python 的软件构建工具,类似于 makecmake,由于使用 Python 语法,编写上更加灵活,适用于复杂的项目场景。GDExtensiongodot-cpp 绑定工具就需要使用 scons 进行编译,所以需要配置 Python 和 scons 环境。

scons 版本依赖于 Python 版本,考虑到之后 scons 的版本更新,不能将基础镜像指定为特定版本的 Python,所以这里选择了 conda 虚拟环境,可以在镜像构建时指定 Python 版本和 scons 版本。conda 虚拟环境可以使用 Anaconda 或者 Miniconda,其中 Anaconda 是一个包含了 condaPython 和超过 150 个科学包及其依赖项的科学 Python 发行版,体量上非常庞大,而 Miniconda 相对轻量,只包含了 condaPython,需要用户手动安装需要的包。MiniConda 的环境更加简洁,适用于 Docker 镜像构建。

Dockerfile 编写

方案参照的是 mrt-prodz/docker-godot-gdextension-builder: Building Godot 4 GDExtension C++ example with Docker

这个方案在本地进行测试时,发现镜像构建时会报错,具体原因是找不到特定版本的 g++-mingw-w64-x86-64。于是我调整了一下配置,在一些地方进行了微调。

基础镜像选择的是 continuumio/miniconda3,默认安装了 conda 环境,方便指定 Python 版本和安装 scons 编译工具。

1
FROM continuumio/miniconda3:latest

这里的 Python 选择的是 3.11.9 版本,一开始我试着配置 Python 3.12.3 + scons 4.7,编译 godot-cpp 时报错找不到 ctypes 模块,努力了下但还是没解决,希望后续哪位同学可以解决这个问题。最新 Python 版本见 Download Python | Python.org

1
2
3
4
5
6
7
8
9
10
SHELL ["/bin/bash", "--login", "-c"]

RUN conda init bash

RUN set -ex \
\
&& conda create -y -n py311 python=3.11.9 \
&& conda activate py311 \
&& conda install -c conda-forge -y scons=${SCONS_VERSION} \
&& ln -s /opt/conda/envs/py311/bin/python /usr/bin/python311

Dockerfile 中使用 conda 命令时,需要注意这是一个交互式命令,首先需要切换终端为 bash,然后使用 conda init bash 初始化 conda 环境,如果不初始化,后续使用 conda 命令会报错 Run 'conda init' before 'conda activate'。之后指定 Python 版本建立激活虚拟环境并安装 scons 编译工具,最后创建一个软链接,方便后续使用。

接下来需要配置 C++ 的编译环境,镜像基于 Debian,使用 apt 包管理器,使用官方源可能会出现部分包下载失败的情况,具体错误为 503 Service Unavailable,这种情况下需要更换为国内源。

1
2
# replace the default sources.list with aliyun mirrors
RUN sed -i 's#http://deb.debian.org/#http://mirrors.aliyun.com/#' /etc/apt/sources.list

然后是安装 MingW-w64 的相关库依赖,可以指定相关版本号。

1
2
3
4
5
6
RUN set -ex \
&& DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
build-essential \
g++-mingw-w64-x86-64=${MINGW_VERSION} \
binutils-mingw-w64=${MINGW_BINUTIL_VERSION}

其中 set -ex 是用来设置 shell 的行为,-e 选项表示当前命令执行失败时,停止执行后续命令,-x 选项表示输出具体的执行命令和过程。

这样 Dockfile 的编写就完成了,可以通过 docker build 命令构建镜像。

1
docker build -t convexwf/godot-gdextension-cpp-builder .

构建 C++ 绑定库

构建的目录结构如下(部分省略):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.
|-- .gitignore
|-- Dockerfile
|-- SConstruct
|-- demo
| |-- .godot
| |-- gdextension
| | |-- gdexample.gdextension
| | `-- libgdexample.windows.template_debug.x86_64.dll
| |-- icon.svg
| |-- icon.svg.import
| `-- project.godot
|-- godot-cpp
| |-- bin
| |-- cmake
| |-- gdextension
| |-- gen
| |-- include
| |-- misc
| |-- src
| |-- test
| `-- tools
`-- src
|-- gdexample.cpp
|-- gdexample.h
|-- register_types.cpp
`-- register_types.h

其中 godot-cppGodot 官方提供的 C++ 绑定库,通过 git clone 下载到本地。

1
git clone -b 4.2 git@github.com:godotengine/godot-cpp.git godot-cpp

src 目录用于存放 GDExtension C++ 的源码,构建产物在 godot-cpp/bin 目录下生成,也可以通过 SConstruct 指定构建目标和生成位置。本文使用的 src 目录下的文件是 Godot 4.2 官方文档中的示例代码,具体见 GDExtension C++ example — Godot Engine (stable) documentation in English

demo 目录用于存放 Godot 项目,一般需要通过 Godot 的编辑器打开。

SConstruct 文件是 scons 编译时的配置文件,可以指定编译目标、编译器、编译选项等,需要用户根据实际需求编写。官方提供了一个 SConstruct 文件 作为参考。

本文涉及的 SConstruct 文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python
import os
import sys

GODOT_PROJECT_NAME = "demo"
env = SConscript("godot-cpp/SConstruct")

env.Append(CPPPATH=["src/"])
sources = Glob("src/*.cpp")

# env['platform'] = windows
# env['target'] = template_debug
# env['suffix'] = .windows.template_debug.x86_64
# env['SHLIBSUFFIX'] = .dll

if env["platform"] == "macos":
library = env.SharedLibrary(
"{}/gdextension/libgdexample.{}.{}.framework/libgdexample.{}.{}".format(
GODOT_PROJECT_NAME, env["platform"], env["target"], env["platform"], env["target"]
),
source=sources,
)
else:
library = env.SharedLibrary(
"demo/gdextension/libgdexample{}{}".format(env["suffix"], env["SHLIBSUFFIX"]),
source=sources,
)

Default(library)

文件中指定 Windows 平台下的最终编译产物为 demo/gdextension/libgdexample.windows.template_debug.x86_64.dll

剩下的文件中,.gitignore 文件来自 godot/.gitignore at master · godotengine/godot

demo/gdexetension 目录下的 gdexample.gdextension 文件是为了能让 Godot 识别载入动态链接库,具体内容会在下一节说明。

因为我们之前已经写好了 Dockerfile,搭配 SConstruct 就可以执行正式构建,执行 shell/build.sh 脚本,或者手动执行下面的命令。

1
2
3
4
docker run --rm \
-v ${WorkingDir}:/app \
convexwf/godot-gdextension-cpp-builder \
bash -c "cd /app/godot-cpp && source activate gde && scons platform=windows custom_api_file=../extension_api.json"

其中 ${WorkingDir} 是当前工作目录,需要替换为实际的绝对路径。注意 Windows 平台下的 git bash 挂载路径需要使用 // 开头,比如 //d/project

第一次构建时需要等待较长时间,笔者的八核笔记本构建时间大约在 10 分钟左右,后续构建时间会缩短。构建产物在 godot-cpp/bindemo/gdextension 目录下。

构建 Godot 项目

新建godot项目

首先通过 Godot 新建项目,也可以从已有项目导入,如上图所示。

demo 目录下的 gdextension 目录是用于存放 GDExtension 的相关文件,libgdexample.windows.template_debug.x86_64.dll 是编译产物,gdexample.gdextension 是 GDExtension 的配置文件,文件命名是任意的,只要保持后缀为 .gdextension 即可被 Godot 识别。其内容如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[configuration]

entry_symbol = "example_library_init"
compatibility_minimum = "4.2"

[libraries]

macos.debug = "res://gdextension/libgdexample.macos.template_debug.framework"
macos.release = "res://gdextension/libgdexample.macos.template_release.framework"
windows.debug.x86_32 = "res://gdextension/libgdexample.windows.template_debug.x86_32.dll"
windows.release.x86_32 = "res://gdextension/libgdexample.windows.template_release.x86_32.dll"
windows.debug.x86_64 = "res://gdextension/libgdexample.windows.template_debug.x86_64.dll"
windows.release.x86_64 = "res://gdextension/libgdexample.windows.template_release.x86_64.dll"
linux.debug.x86_64 = "res://gdextension/libgdexample.linux.template_debug.x86_64.so"
linux.release.x86_64 = "res://gdextension/libgdexample.linux.template_release.x86_64.so"
linux.debug.arm64 = "res://gdextension/libgdexample.linux.template_debug.arm64.so"
linux.release.arm64 = "res://gdextension/libgdexample.linux.template_release.arm64.so"
linux.debug.rv64 = "res://gdextension/libgdexample.linux.template_debug.rv64.so"
linux.release.rv64 = "res://gdextension/libgdexample.linux.template_release.rv64.so"
android.debug.x86_64 = "res://gdextension/libgdexample.android.template_debug.x86_64.so"
android.release.x86_64 = "res://gdextension/libgdexample.android.template_release.x86_64.so"
android.debug.arm64 = "res://gdextension/libgdexample.android.template_debug.arm64.so"
android.release.arm64 = "res://gdextension/libgdexample.android.template_release.arm64.so"

字段说明:

  • entry_symbol:声明模块的入口函数,和 C++ 源码 register_types.cpp 中的函数名保持一致。
  • compatibility_minimum:最低兼容版本,必填。
  • libraries:声明不同平台下需要识别的动态链接库路径。

保存后查看 Godot 编辑器的输出日志,如果出现 GDExtension configuration file must contain a "configuration/entry_symbol" key: res://gdextension/gdexample.gdextension 等类似错误,可以把 *.gdextension 中的注释和多余空行都删去再看看。

如果一切正常,可以在节点中找到插件 GDExample,之后将其添加到场景中即可。之后的步骤不再赘述,可以参考官方文档进行操作。

总结和后续

至此,我们已经完成了 GDExtension C++ 的环境搭建和构建,通过 Docker 镜像的方式,可以在不同的操作系统下快速搭建开发工作流,提高开发效率。后续开发过程中笔者还会持续调整和优化这个方案,欢迎大家多提 issue 和 PR,友好交流,共同进步!

Reference