Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
GDExtension C++ 示例¶
前言¶
The C++ bindings for GDExtension are built on top of the C GDExtension API and provide a nicer way to "extend" nodes and other built-in classes in Godot using C++. This new system allows the extension of Godot to nearly the same level as statically linked C++ modules.
You can download the included example in the test folder of the godot-cpp repository on GitHub.
设置项目¶
你需要一些先决条件:
Godot 4 可执行文件,
C++ 编译器,
SCons 作为构建工具,
godot-cpp 仓库的副本。
另请参阅《编译》,因为构建工具与从源代码编译 Godot 所需的构建工具相同。
You can download the godot-cpp repository from GitHub or let Git do the work for you. Note that this repository has different branches for different versions of Godot. GDExtensions will not work in older versions of Godot (only Godot 4 and up) and vice versa, so make sure you download the correct branch.
备注
To use GDExtension
you need to use the godot-cpp branch that matches the version of Godot that you are
targeting. For example, if you're targeting Godot 4.1, use the 4.1
branch,
which is what is shown through out this tutorial.
The master
branch is the development branch which is updated regularly
to work with Godot's master
branch.
警告
Our long-term goal is that GDExtensions targeting an earlier version of Godot will work in later minor versions, but not vice-versa. For example, a GDExtension targeting Godot 4.2 should work just fine in Godot 4.3, but one targeting Godot 4.3 won't work in Godot 4.2.
However, GDExtension is currently experimental, which means that we may break compatibility in order to fix major bugs or include critical features. For example, GDExtensions created for Godot 4.0 aren't compatible with Godot 4.1 (see 将 GDExtension 更新到 4.1).
If you are versioning your project using Git, it is recommended to add it as a Git submodule:
mkdir gdextension_cpp_example
cd gdextension_cpp_example
git init
git submodule add -b 4.1 https://github.com/godotengine/godot-cpp
cd godot-cpp
git submodule update --init
Alternatively, you can also clone it to the project folder:
mkdir gdextension_cpp_example
cd gdextension_cpp_example
git clone -b 4.1 https://github.com/godotengine/godot-cpp
备注
If you decide to download the repository or clone it into your folder, make sure to keep the folder layout the same as we've setup here. Much of the code we'll be showcasing here assumes the project has this layout.
如果从介绍中指定的链接克隆示例, 子模块不会自动初始化. 你需要执行以下命令:
cd gdextension_cpp_example
git submodule update --init
This will initialize the repository in your project folder.
构建 C++ 绑定¶
现在我们已经下载了我们的先决条件, 现在是构建C++绑定的时候了.
仓库包含当前 Godot 版本的元数据副本,但如果你需要为较新版本的 Godot 构建这些绑定,只需调用 Godot 可执行文件:
godot --dump-extension-api
The resulting extension_api.json
file will be created in the executable's
directory. Copy it to the project folder and add custom_api_file=<PATH_TO_FILE>
to the scons command below.
To generate and compile the bindings, use this command (replacing <platform>
with windows
, linux
or macos
depending on your OS):
The build process automatically detects the number of CPU threads to use for
parallel builds. To specify a number of CPU threads to use, add -jN
at the
end of the SCons command line where N
is the number of CPU threads to use.
cd godot-cpp
scons platform=<platform> custom_api_file=<PATH_TO_FILE>
cd ..
这一步将需要一段时间. 完成后, 你应该有一个静态库, 可以编译到你的项目中, 存储在 godot-cpp / bin /
中.
备注
你可能需要在 Windows 或 Linux 的命令行中添加 bits=64
。
创建一个简单的插件¶
现在是构建实际插件的时候了. 我们首先创建一个空的Godot项目, 我们将在其中放置一些文件.
Open Godot and create a new project. For this example, we will place it in a
folder called demo
inside our GDExtension's folder structure.
在我们的演示项目中, 我们将创建一个包含名为 "Main" 的节点的场景, 我们将其保存为 main.tscn
. 我们稍后再回过头来看看.
Back in the top-level GDExtension module folder, we're also going to create a
subfolder called src
in which we'll place our source files.
You should now have demo
, godot-cpp
, and src
directories in your GDExtension module.
Your folder structure should now look like this:
gdextension_cpp_example/
|
+--demo/ # game example/demo to test the extension
|
+--godot-cpp/ # C++ bindings
|
+--src/ # source code of the extension we are building
In the src
folder, we'll start with creating our header file for the
GDExtension node we'll be creating. We will name it gdexample.h
:
#ifndef GDEXAMPLE_H
#define GDEXAMPLE_H
#include <godot_cpp/classes/sprite2d.hpp>
namespace godot {
class GDExample : public Sprite2D {
GDCLASS(GDExample, Sprite2D)
private:
double time_passed;
protected:
static void _bind_methods();
public:
GDExample();
~GDExample();
void _process(double delta) override;
};
}
#endif
There are a few things of note to the above. We include sprite2d.hpp
which
contains bindings to the Sprite2D class. We'll be extending this class in our
module.
We're using the namespace godot
, since everything in GDExtension is defined
within this namespace.
Then we have our class definition, which inherits from our Sprite2D through a
container class. We'll see a few side effects of this later on. The
GDCLASS
macro sets up a few internal things for us.
之后, 我们声明一个名为 time_passed
的成员变量.
In the next block we're defining our methods, we have our constructor and destructor defined, but there are two other functions that will likely look familiar to some, and one new method.
The first is _bind_methods
, which is a static function that Godot will
call to find out which methods can be called and which properties it exposes.
The second is our _process
function, which will work exactly the same
as the _process
function you're used to in GDScript.
所以, 让我们通过创建 gdexample.cpp
文件来实现我们的函数:
#include "gdexample.h"
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void GDExample::_bind_methods() {
}
GDExample::GDExample() {
// Initialize any variables here.
time_passed = 0.0;
}
GDExample::~GDExample() {
// Add your cleanup here.
}
void GDExample::_process(double delta) {
time_passed += delta;
Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 + (10.0 * cos(time_passed * 1.5)));
set_position(new_position);
}
This one should be straightforward. We're implementing each method of our class that we defined in our header file.
Note our _process
function, which keeps track of how much time has passed
and calculates a new position for our sprite using a sine and cosine function.
There is one more C++ file we need; we'll name it register_types.cpp
. Our
GDExtension plugin can contain multiple classes, each with their own header
and source file like we've implemented GDExample
up above. What we need now
is a small bit of code that tells Godot about all the classes in our
GDExtension plugin.
#include "register_types.h"
#include "gdexample.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
using namespace godot;
void initialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
ClassDB::register_class<GDExample>();
}
void uninitialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT example_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_example_module);
init_obj.register_terminator(uninitialize_example_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}
The initialize_example_module
and uninitialize_example_module
functions get
called respectively when Godot loads our plugin and when it unloads it. All
we're doing here is parse through the functions in our bindings module to
initialize them, but you might have to set up more things depending on your
needs. We call the function register_class
for each of our classes in our library.
The important function is the third function called example_library_init
.
We first call a function in our bindings library that creates an initialization object.
This object registers the initialization and termination functions of the GDExtension.
Furthermore, it sets the level of initialization (core, servers, scene, editor, level).
At last, we need the header file for the register_types.cpp
named
register_types.h
.
#ifndef GDEXAMPLE_REGISTER_TYPES_H
#define GDEXAMPLE_REGISTER_TYPES_H
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void initialize_example_module(ModuleInitializationLevel p_level);
void uninitialize_example_module(ModuleInitializationLevel p_level);
#endif // GDEXAMPLE_REGISTER_TYPES_H
编译插件¶
手工编写 SCons 用于构建的 SConstruct
文件并不容易。出于这个示例的目的,只需使用我们已经准备好的这个硬编码的 SConstruct 文件
。我们将在后续教程中介绍如何使用这些构建文件的更可定制的详细示例。
备注
This SConstruct
file was written to be used with the latest godot-cpp
master, you may need to make small changes using it with older versions or
refer to the SConstruct
file in the Godot 4.0 documentation.
Once you've downloaded the SConstruct
file, place it in your GDExtension folder
structure alongside godot-cpp
, src
and demo
, then run:
scons platform=<platform>
你现在应该能够在