内容学习文档时制作
GDExtension 介绍:What is GDExtension? — Godot Engine (latest) documentation in English
一个教程文章:Introducing GDNative’s successor, GDExtension (godotengine.org)
你要确保已经下载安装了:
- git
-
scons 构建工具
-
c++编译器
windows 上最好安装微软的 visual studio 不容易出问题
其他 MinGW 相关:
- MinGW msys2-distrib安装包下载_开源镜像站-阿里云 (aliyun.com),下载合适的版本,我下载的是 msys2-x86_64-latest.exe
- Win11下Clion+MSYS2(MinGW64)配置C++编译环境_clion msys2_RangerKnight的博客-CSDN博客
- godot 编辑器
如果没有安装请看文档:Compiling for Windows — Godot Engine (stable) documentation in English
配置好环境之后,开始以下步骤:
编写代码
进入一个目录内,比如我进入 D:\ 盘的 godot 文件夹,创建一个 gde_test 目录
cd 'd:\godot\cpp'
mkdir gde_test
cd gde_test
git 克隆下载 godot-cpp 依赖文件,并初始化为 git 子模块
git init
git submodule add -b master https://github.com/godotengine/godot-cpp
cd godot-cpp
git submodule update --init
如果您正在使用 Git 对项目进行版本控制,那么最好将其添加为 Git 子模块
进入 godot-cpp 构建一遍代码
cd godot-cpp
scons platform=windows
使用指定 Godot 编辑器构建绑定,在 godot-cpp 目录里执行以下命令。
D:\godot\Engine\Godot_v4.0-stable_win64.exe --dump-extension-api extension_api.json
我的 Godot 编辑器路径为 D:\godot\Engine\Godot_v4.0-stable_win64.exe 所以是上面的路径
如果不这样做,godot-cpp 里的代码编译后会是过时的代码
在 gde_test 下目录结构,创建 demo 和 src 文件夹
|-gde_test
|-demo # Godot 游戏测试项目
|-godot-cpp/ # 构建源码时所需的文件
|-src # 自己写的 c++ 对 Godot 扩展的源码
src 文件夹中创建 gdexample.h 文件,代码
#ifndef GDEXAMPLE_H
#define GDEXAMPLE_H
#include <godot_cpp/classes/sprite2d.hpp>
namespace godot {
class GDExample : public Sprite2D {
GDCLASS(GDExample, Sprite2D)
private:
float time_passed;
protected:
static void _bind_methods();
public:
GDExample();
~GDExample();
void _process(float delta);
};
}
#endif
再创建 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(float 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);
}
以上是自定义的任意类,然后需要创建注册这些类的文件
register_types.h
//
// Created by z on 2023/3/26.
//
#ifndef GDE_TEST_REGISTER_TYPES_H
#define GDE_TEST_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 //GDE_TEST_REGISTER_TYPES_H
register_types.cpp
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/godot.hpp>
#include "gdexample.h"
using namespace godot;
void initialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
// 注册类
/*ClassDB::register_class<ExampleVirtual>(true);
ClassDB::register_abstract_class<ExampleAbstract>();*/
ClassDB::register_class<GDExample>();
}
void uninitialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
// 下面的 example_library_init 为接口名称
extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT
example_library_init(const GDExtensionInterface *p_interface, GDExtensionClassLibraryPtr p_library,
GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_interface, 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();
}
}
下载 SConstruct (用于构建当前写的源码)文件放到 gde_test 目录下
文档对这个文件的描述:We cannot easily write by hand a
SConstruct
file that SCons would use for building. For the purpose of this example, just usethis hardcoded SConstruct file
we’ve prepared. We’ll cover a more customizable, detailed example on how to use these build files in a subsequent tutorial.
输入命令对刚刚的文件进行构建一遍
scons -Q
注意下载的 SConstruct 文件,里面包含有两行代码
env.Append(CPPPATH=["src/"]) sources = Glob("src/*.cpp")
src/
和src/*.cpp
要编译的源码文件,*
代表 src 目录下所有 cpp 文件,如果有其他目录,则还要加上src/*/*.cpp
扫描的目录,否则会出现包含下面信息系的错误register_types.windows.template_debug.x86_64.obj : error LNK2019: 无法解析的外部符号 "protected: static void __cdecl godot::GDExample::_ bind_methods(void)" (?_bind_methods@GDExample@godot@@KAXXZ),函数 "private: static void __cdecl godot::ClassDB::_register_class<class godot::GDExample,0>(bool)" (??_register_class@VGDExample@godot@@0A@@ClassDB@godot@@CAX_N@Z) 中引用了该符号 register_types.windows.template_debug.x86_64.obj : error LNK2019: 无法解析的外部符号 "public: __cdecl godot::GDExample::GDExample(void)" (??0GDExample@godot@@QEAA@XZ),函数 "public: static void * __cdecl godot::GDExample::create(void *)" (?create@GDExample@godot@@SAPEAXPEAX@Z) 中引用了该符号 demo\bin\libgdexample.windows.template_debug.x86_64.dll : fatal error LNK1120: 3 个无法解析的外部命令
也可能是:有定义未实现,或者说编辑器找不到实现。有可能是你给ide设置了文件包含路径,写代码没问题,但是编译器方面并没设置。也有可能就是单纯头文件有定义就是cpp没实现
- 代码里还包含有个路径:
demo/bin/libgdexample
。这个是要编译到的路径和libgdexample
文件名,如果修改可以更换要编译到的路径位置关于编译的文档:Compiling for Windows — Godot Engine (latest) documentation in English
然后在 Godot 那个 demo 项目里,添加一个 example.gdextension 文件,写入如下代码进行添加插件
[configuration]
entry_symbol = "example_library_init"
[libraries]
macos.debug = "res://bin/libgdexample.macos.template_debug.framework"
macos.release = "res://bin/libgdexample.macos.template_release.framework"
windows.debug.x86_32 = "res://bin/libgdexample.windows.template_debug.x86_32.dll"
windows.release.x86_32 = "res://bin/libgdexample.windows.template_release.x86_32.dll"
windows.debug.x86_64 = "res://bin/libgdexample.windows.template_debug.x86_64.dll"
windows.release.x86_64 = "res://bin/libgdexample.windows.template_release.x86_64.dll"
linux.debug.x86_64 = "res://bin/libgdexample.linux.template_debug.x86_64.so"
linux.release.x86_64 = "res://bin/libgdexample.linux.template_release.x86_64.so"
linux.debug.arm64 = "res://bin/libgdexample.linux.template_debug.arm64.so"
linux.release.arm64 = "res://bin/libgdexample.linux.template_release.arm64.so"
linux.debug.rv64 = "res://bin/libgdexample.linux.template_debug.rv64.so"
linux.release.rv64 = "res://bin/libgdexample.linux.template_release.rv64.so"
android.debug.x86_64 = "res://bin/libgdexample.android.template_debug.x86_64.so"
android.release.x86_64 = "res://bin/libgdexample.android.template_release.x86_64.so"
android.debug.arm64 = "res://bin/libgdexample.android.template_debug.arm64.so"
android.release.arm64 = "res://bin/libgdexample.android.template_release.arm64.so"
代码中
entry_symbol = "example_library_init"
里的example_library_init
要和之前的 register_types.cpp 文件中的那个保持一致,否则会找不到
libraries
部分代表对应的平台的编译的文件路径,根据需要进行更改
写代码时可以参考源码:godot/node.cpp at 4.0 · godotengine/godot (github.com) 末尾的绑定方法、信号、属性的代码
我这里有个稍微写得 SConstruct 文件,就是一个 Python 文件
下面
注意下面不能有中文字符串,否则 scons 命令运行会报错
#!/usr/bin/env python
import os
import sys
env = SConscript("godot-cpp/SConstruct")
# For reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
# - CXXFLAGS are for C++-specific compilation flags
# - CPPFLAGS are for pre-processor flags
# - CPPDEFINES are for pre-processor defines
# - LINKFLAGS are for linking flags
def scan_files(directory, prefix=None, postfix=None):
files_list = []
for root, sub_dirs, files in os.walk(directory):
for special_file in files:
if postfix:
if special_file.endswith(postfix):
files_list.append(os.path.join(root, special_file))
elif prefix:
if special_file.startswith(prefix):
files_list.append(os.path.join(root, special_file))
else:
files_list.append(os.path.join(root, special_file))
return files_list
# tweak this if you want to use different folders, or more folders, to store your source code in.
env.Append(CPPPATH=["src/", "src/plugin"])
# Sources to compile
sources = []
sources.extend(scan_files("src/", None, "cpp"))
if env["platform"] != "macos":
library = env.SharedLibrary(
"demo/bin/libgdexample{}{}".format(env["suffix"], env["SHLIBSUFFIX"]),
source=sources,
)
else:
library = env.SharedLibrary(
"demo/bin/libgdexample.{}.{}.framework/libgdexample.{}.{}".format(
env["platform"], env["target"], env["platform"], env["target"]
),
source=sources,
)
Default(library)
写代码时要注意 Callable 还不能使用 bind 方法:https://github.com/godotengine/godot-cpp/issues/802
其他代码测试
summator.h
#include "godot_cpp/classes/ref_counted.hpp"
namespace godot {
class Summator : public RefCounted {
GDCLASS(Summator, RefCounted);
int count;
protected:
static void _bind_methods();
public:
void add(int p_value);
void reset();
int get_total() const;
Summator();
};
}
summator.cpp
#include "summator.h"
using namespace godot;
void Summator::add(int p_value) {
count += p_value;
}
void Summator::reset() { count = 0; }
int Summator::get_total() const { return count; }
void Summator::_bind_methods() {
ClassDB::bind_method(D_METHOD("add"), &Summator::add);
ClassDB::bind_method(D_METHOD("reset"), &Summator::reset);
ClassDB::bind_method(D_METHOD("get_total"), &Summator::get_total);
}
Summator::Summator() { count = 0; }
Summator::~Summator() {};