chore(build): 搭建并验证 C# / C++(GDExtension) 原生构建环境(4.7.1 Mono)

为「GDScript→C#→C++」性能逃生梯备好可随时启用的工具链,运行时仍 100% GDScript。

C#:新增根 Rogue.csproj(Godot.NET.Sdk/4.7.1,net8.0,程序集名 Rogue);
    csharp/ 下既有 3 个骨架 .cs 现可编译,dotnet build 与编辑器 --build-solutions 均 0 错通过。

C++:gdextension/ 下加 godot-cpp 子模块(master,因无 4.6/4.7 分支,配合本工程 dump 的
    4.7.1 extension_api.json + gdextension_interface.h 经 custom_api_file 精确匹配 ABI);
    SConstruct + 示例扩展 SpellforgeNative(含 PackedFloat32Array 零拷贝热路径写法);
    SCons 自动探测 MSVC 构建出 debug .dll,headless 冒烟验证类注册/调用通过
    (CLASS_EXISTS=true / multiply=42 / scale_inplace 正确)。

配套:.gitignore 忽略编译中间产物、保留 debug .dll 开箱即用;gdextension/README.md 记录构建/激活/升级流程;
    CLAUDE.md 修正引擎版本 4.6→4.7.1 并更新「环境已就绪但休眠」现状。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-23 15:53:39 +08:00
co-authored by Claude Opus 4.8
parent 0561377492
commit aa69c0e2d4
16 changed files with 360365 additions and 5 deletions
+91
View File
@@ -0,0 +1,91 @@
# 原生环境(C# / C++)搭建说明
本工程为 **Godot 4.7.1-stable (Mono/.NET)** 工程,性能优化采用逐级逃生梯
**GDScript(默认)→ C#(热路径逃生舱)→ C++(极限内核)**。语言定位与使用门槛见
[`CLAUDE.md`](../CLAUDE.md)「编程语言规范」。
> 现状:**运行时仍 100% GDScript**。C#/C++ 环境已搭好并冒烟验证通过,属「就绪但休眠」的逃生舱,
> 仅当 profile 证明瓶颈时才按 `ADR-L1` 启用。
---
## C#.NET
已就绪,无需额外步骤:
- 工程文件 `Rogue.csproj`(根目录,`Godot.NET.Sdk/4.7.1``net8.0`,程序集名 `Rogue` 对齐
`project.godot [dotnet] project/assembly_name`)。
- C# 源码放 `csharp/`,由 SDK 默认 `**/*.cs` glob 自动纳入编译。现有
`csharp/{autoloads,systems}/*.cs` 为休眠骨架。
- 依赖:.NET SDK 8/9/10(本机已装)。
**构建 / 验证:**
```bash
dotnet build Rogue.csproj -c Debug # 命令行构建
# 或在 Godot 编辑器内点「构建」;headless:
Godot_v4.7.1-stable_mono_win64.exe --headless --build-solutions --quit
```
产物在 `.godot/mono/temp/bin/<Config>/Rogue.dll`(已被 `.gitignore` 忽略)。
**新增 C# 类**:在 `csharp/` 下建 `.cs``public partial class Foo : Node/RefCounted`,重新构建即可
attach 到节点或注册为 autoload。
---
## C++GDExtension
**目录结构:**
```
gdextension/
godot-cpp/ 子模块(master + 本工程 4.7.1 API 覆盖)
api/ 从 4.7.1 编辑器 dump 的 extension_api.json + gdextension_interface.h
src/ 扩展源码(register_types + 示例类 SpellforgeNative
SConstruct 构建脚本(custom_api_file 指向 api/extension_api.json
bin/ 产物:*.gdextension + 已构建的 debug .dll
verify_native.gd headless 冒烟脚本
```
**首次准备(克隆后):**
```bash
git submodule update --init --recursive # 拉取 godot-cpp
python -m pip install scons # 若未装
```
**构建:**
```bash
cd gdextension
scons target=template_debug -j8 # 或 python -m SCons ...
scons target=template_release -j8 # 发布版
```
- Windows 由 SCons 自动探测 MSVCVS 2022),无需手动 vcvars。
- 产物:`gdextension/bin/libspellforge_native.windows.template_debug.x86_64.dll`
为开箱即用,**debug .dll 已提交**;中间产物(`*.obj/*.lib/*.exp/.sconsign.dblite`)已忽略。
**激活 / 验证:**
`bin/spellforge_native.gdextension` 描述库路径与入口符号。Godot 编辑器扫描 `res://` 时自动发现并
写入 `.godot/extension_list.cfg`(该文件在 `.godot/` 内、被忽略)。**若编辑器已在运行,需重启编辑器**
才能加载新扩展。
```bash
# headless 冒烟:断言类已注册且可调用
Godot_v4.7.1-stable_mono_win64.exe --headless --path . -s res://gdextension/verify_native.gd
# 预期输出:CLASS_EXISTS=true / MULTIPLY_6x7=42.0 / SCALE_INPLACE_x10=[10,20,30]
```
**新增 C++ 类**:在 `src/` 下建 `.h/.cpp``GDCLASS` 宏),在 `register_types.cpp`
`initialize_spellforge_module``GDREGISTER_CLASS(YourClass);`,重新 `scons` 即可。
**升级引擎版本时**:重新 dump API 覆盖 `api/`,并 `git submodule update --remote gdextension/godot-cpp` 后重建。
```bash
Godot_....exe --headless --dump-extension-api extension_api.json --quit
Godot_....exe --headless --dump-gdextension-interface --quit
# 将产物移入 gdextension/api/ 覆盖旧文件
```
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python
# SConstruct — Spellforge C++ GDExtension 构建脚本
# 用法(在本目录 gdextension/ 下):
# scons # 默认 template_debug,本机平台/架构
# scons target=template_release
# scons -j8 # 并行编译
# 依赖:godot-cpp 子模块(gdextension/godot-cpp)、SCons、C++ 工具链(Windows 用 MSVCSCons 自动探测 VS)。
# API:通过 custom_api_file 指向本工程从 4.7.1 编辑器 dump 的 extension_api.json,保证绑定与引擎精确匹配。
import os
# 将本工程 4.7.1 的 API 注入 godot-cpp(除非命令行显式覆盖 custom_api_file)。
ARGUMENTS.setdefault("custom_api_file", os.path.abspath("api/extension_api.json"))
# 复用 godot-cpp 的构建环境(平台/架构/编译器探测、绑定生成、静态库)。
env = SConscript("godot-cpp/SConstruct")
env.Append(CPPPATH=["src/"])
sources = Glob("src/*.cpp")
libname = "spellforge_native"
bindir = "bin"
if env["platform"] == "macos":
target = "{}/lib{}.{}.{}.framework/lib{}.{}.{}".format(
bindir, libname, env["platform"], env["target"],
libname, env["platform"], env["target"],
)
else:
target = "{}/lib{}{}{}".format(bindir, libname, env["suffix"], env["SHLIBSUFFIX"])
library = env.SharedLibrary(target, source=sources)
Default(library)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
[configuration]
entry_symbol = "spellforge_library_init"
compatibility_minimum = "4.5"
reloadable = true
[libraries]
windows.debug.x86_64 = "res://gdextension/bin/libspellforge_native.windows.template_debug.x86_64.dll"
windows.release.x86_64 = "res://gdextension/bin/libspellforge_native.windows.template_release.x86_64.dll"
linux.debug.x86_64 = "res://gdextension/bin/libspellforge_native.linux.template_debug.x86_64.so"
linux.release.x86_64 = "res://gdextension/bin/libspellforge_native.linux.template_release.x86_64.so"
macos.debug = "res://gdextension/bin/libspellforge_native.macos.template_debug.framework"
macos.release = "res://gdextension/bin/libspellforge_native.macos.template_release.framework"
+40
View File
@@ -0,0 +1,40 @@
// register_types.cpp — GDExtension 模块注册入口
// 新增 C++ 类:在 initialize 里 GDREGISTER_CLASS(YourClass); 并包含其头文件。
#include "register_types.h"
#include "spellforge_native.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
using namespace godot;
void initialize_spellforge_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
GDREGISTER_CLASS(SpellforgeNative);
}
void uninitialize_spellforge_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C" {
// 入口符号,须与 .gdextension 的 entry_symbol 一致。
GDExtensionBool GDE_EXPORT spellforge_library_init(
GDExtensionInterfaceGetProcAddress p_get_proc_address,
GDExtensionClassLibraryPtr p_library,
GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_spellforge_module);
init_obj.register_terminator(uninitialize_spellforge_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}
+9
View File
@@ -0,0 +1,9 @@
// register_types.h — GDExtension 模块注册入口声明
#pragma once
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void initialize_spellforge_module(ModuleInitializationLevel p_level);
void uninitialize_spellforge_module(ModuleInitializationLevel p_level);
+34
View File
@@ -0,0 +1,34 @@
// spellforge_native.cpp — 见头文件说明。
#include "spellforge_native.h"
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void SpellforgeNative::_bind_methods() {
ClassDB::bind_method(D_METHOD("multiply", "a", "b"), &SpellforgeNative::multiply);
ClassDB::bind_method(D_METHOD("native_info"), &SpellforgeNative::native_info);
ClassDB::bind_method(D_METHOD("scale_inplace", "data", "factor"), &SpellforgeNative::scale_inplace);
}
SpellforgeNative::SpellforgeNative() {}
SpellforgeNative::~SpellforgeNative() {}
double SpellforgeNative::multiply(double a, double b) const {
return a * b;
}
String SpellforgeNative::native_info() const {
return String("Spellforge C++ GDExtension OK — built against Godot 4.7.1 API");
}
PackedFloat32Array SpellforgeNative::scale_inplace(PackedFloat32Array data, float factor) const {
// 边界纪律(ADR-L1):热循环全程在 C++ 内完成,仅在边界批量进出 PackedArray。
// ptrw() 拿到原生连续内存指针,内循环零 GodotObject 调用。
const int n = data.size();
float *w = data.ptrw();
for (int i = 0; i < n; i++) {
w[i] *= factor;
}
return data;
}
+28
View File
@@ -0,0 +1,28 @@
// spellforge_native.h — C++ GDExtension 冒烟示例类
// 用途:证明 C++GDExtension)环境可用。真实极限内核见 CLAUDE.md「编程语言规范」。
#pragma once
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/variant/packed_float32_array.hpp>
namespace godot {
class SpellforgeNative : public RefCounted {
GDCLASS(SpellforgeNative, RefCounted)
protected:
static void _bind_methods();
public:
SpellforgeNative();
~SpellforgeNative();
// 冒烟:标量运算
double multiply(double a, double b) const;
// 冒烟:返回原生构建信息字符串
String native_info() const;
// 冒烟:原地就地缩放 PackedFloat32Array(演示零拷贝热路径写法:AsSpan 等价的 ptrw 直写)
PackedFloat32Array scale_inplace(PackedFloat32Array data, float factor) const;
};
} // namespace godot
+13
View File
@@ -0,0 +1,13 @@
extends SceneTree
# 临时冒烟脚本:headless 验证 C++ GDExtension 是否被加载、类是否可实例化调用。
# 用法:godot --headless --path . -s res://gdextension/verify_native.gd
func _init() -> void:
var ok := ClassDB.class_exists("SpellforgeNative")
print("CLASS_EXISTS=", ok)
if ok:
var n: Object = ClassDB.instantiate("SpellforgeNative")
print("INFO=", n.native_info())
print("MULTIPLY_6x7=", n.multiply(6.0, 7.0))
var arr := PackedFloat32Array([1.0, 2.0, 3.0])
print("SCALE_INPLACE_x10=", n.scale_inplace(arr, 10.0))
quit()