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
+5 -5
View File
@@ -4,20 +4,20 @@
## 项目简介
Spellforge(万法熔炉)—— Godot 4.6 (Mono) 弹幕射击 RogueliteSoA / ECS-Lite 架构,法术管道系统。工程概览见 [`README.md`](README.md)。
Spellforge(万法熔炉)—— Godot 4.7.1-stable (Mono/.NET) 弹幕射击 RogueliteSoA / ECS-Lite 架构,法术管道系统。工程概览见 [`README.md`](README.md)。
## 编程语言规范(GDScript / C#
## 编程语言规范(GDScript / C# / C++
本工程为 Mono 工程,为追求性能允许 **GDScript / C# / C++** 三语共存,但**各有明确定位、按需逐级下探,绝非平均混用**。核心原则:**用对语言,而非用多语言**;发挥各自长处,以「先测量后优化」驱动性能,不做无凭据的过早优化。性能优化路线是一条**逐级升级的逃生梯**:GDScript(默认)→ C#(热路径逃生舱)→ C++(极限内核),**仅当上一级被 profile 证明不够时才下探下一级**。
**现状(务必知悉)**:当前**运行时 100% 为 GDScript** —— 全部 42 个 autoload、法术 VM、子弹/敌人/空间网格等所有热循环均是 GDScript,实测 500 敌 + 1500 弹 `_physics_process` ≈ 0.6ms(约帧预算 3.6%),性能充裕。`csharp/` 下的 3 个 `.cs` 文件是**未激活的死骨架**`.csproj`从未实例化、`_cs_node` 恒为 `null` 走 GD 回退),**不要假设它们在运行**。C# 热路径迁移是文档记录但**已无限期推迟**的方案(风险 `R-08` / `ADR-L1`)。
**现状(务必知悉)**:当前**运行时 100% 为 GDScript** —— 全部 42 个 autoload、法术 VM、子弹/敌人/空间网格等所有热循环均是 GDScript,实测 500 敌 + 1500 弹 `_physics_process` ≈ 0.6ms(约帧预算 3.6%),性能充裕。C#/C++ **构建环境已搭好并冒烟验证通过**`Rogue.csproj` + `gdextension/` godot-cpp 子模块 + 示例扩展,详见 [`gdextension/README.md`](gdextension/README.md)),但属**「就绪但休眠」的逃生舱**:`csharp/` 下的 3 个 `.cs` 虽可编译,仍是**未激活的死骨架**(从未实例化、`_cs_node` 恒为 `null` 走 GD 回退),C++ 侧仅有冒烟示例类 —— **不要假设它们在运行**热路径迁移是文档记录但**尚未触发**的方案(风险 `R-08` / `ADR-L1`,环境就绪 ≠ 已迁移
- **GDScript 为默认与主力语言**:游戏逻辑、系统管线、UI、`@tool` 编辑器插件、数据驱动胶水(JSON 加载/注册表/事件)、场景树与信号交互 —— 一律用 GDScript。它的长处是迭代快、与引擎/场景树/信号无缝、动态灵活。**新功能默认写 GDScript**,除非有实测数据证明是瓶颈。
- GDScript 内的性能纪律:热循环用 SoA(`PackedFloat32Array` / `PackedInt32Array` + 整数实体 ID),避免每帧 `new`/字典/lambda;分支用 `match`/`for` 而非闭包;变量与函数签名标注静态类型(`: float` / `-> void`)以启用引擎优化;缓存跨节点引用,勿在内循环反复 `get_node`
- **C# 仅为「热路径逃生舱」****唯一**动机是 GDScript 已被 profile 证明扛不住的密集数值内循环(大规模弹幕积分、Boid 分离、空间哈希)。C# 长处是原生吞吐、真值类型 `struct``Span<T>` 零拷贝、纯 C# 循环内无每次调用的封送开销。**除此之外不要引入 C#。**
- 引入门槛(缺一不可):① 有 profile 数据佐证瓶颈;② 补齐可编译的 `.csproj`(当前不存在);③ 遵循 `docs/technical/architecture_design.md` §6.1 / `ADR-L1` 热路径三铁律 —— **(a)** 内循环**禁止** `GodotObject.Call()` / `.Set()`(跨语言封送即性能杀手);**(b)** 经 `PackedFloat32Array.AsSpan()` 零拷贝直读 SoA 数组;**(c)** 事件经 `EventBus.emit_batch` 批量通知,勿逐个跨界回调。
- 引入门槛(缺一不可):① 有 profile 数据佐证瓶颈;② 环境已就绪(`Rogue.csproj` + `csharp/` 已可编译,见 [`gdextension/README.md`](gdextension/README.md)),启用即在此之上落地 C# 内核;③ 遵循 `docs/technical/architecture_design.md` §6.1 / `ADR-L1` 热路径三铁律 —— **(a)** 内循环**禁止** `GodotObject.Call()` / `.Set()`(跨语言封送即性能杀手);**(b)** 经 `PackedFloat32Array.AsSpan()` 零拷贝直读 SoA 数组;**(c)** 事件经 `EventBus.emit_batch` 批量通知,勿逐个跨界回调。
- 跨语言边界成本真实(`R-08`):**热数据与热循环须落在边界同一侧**,仅在边界处批量进出。切忌把一个循环拆成 GD↔C# 反复横跳。
- **C++GDExtension)为「极限内核」最深一级**:**唯一**动机是连 C# 都被 profile 证明不够、且属计算密集到值得动用原生手段的核心内核(如 SIMD 向量化的弹幕/碰撞积分、需手动内存布局与缓存对齐的空间结构)。C++ 长处是无托管开销、可控内存布局、SIMD/内联汇编、直贴 `PackedArray` 底层指针。代价也最高:需 `godot-cpp` 子模块 + SConstruct/CMake 交叉编译工具链、按平台产出 `.dll`/`.so`/`.dylib`、调试与移植成本远高于前两级 —— **绝不为「感觉会更快」而引入,必须有前一级的实测瓶颈数据背书。** 当前工程**尚无任何 GDExtension**,引入即新增构建维度,须先在文档立项(对齐 `ADR-L1` 同源精神)。
- **C++GDExtension)为「极限内核」最深一级**:**唯一**动机是连 C# 都被 profile 证明不够、且属计算密集到值得动用原生手段的核心内核(如 SIMD 向量化的弹幕/碰撞积分、需手动内存布局与缓存对齐的空间结构)。C++ 长处是无托管开销、可控内存布局、SIMD/内联汇编、直贴 `PackedArray` 底层指针。代价也最高:需 `godot-cpp` 子模块 + SCons 工具链、按平台产出 `.dll`/`.so`/`.dylib`、调试与移植成本远高于前两级 —— **绝不为「感觉会更快」而引入,必须有前一级的实测瓶颈数据背书。** GDExtension 环境已搭好(`gdextension/` 内 godot-cpp 子模块 + `SConstruct` + 冒烟示例 `SpellforgeNative`,构建/激活见 [`gdextension/README.md`](gdextension/README.md));在此之上加类即可,但仍须先在文档立项(对齐 `ADR-L1` 同源精神)。
- C++ 侧同样遵循边界纪律:接口经 `GDExtension` 绑定暴露给 GDScript 壳,热循环全程在 C++ 内完成,仅在边界批量传 `PackedArray`(零拷贝取原生指针),不在内循环回调 GDScript/C#。
- **接口壳 + 原生内核模式**(如某系统确需 C# 或 C++ 内核):GDScript 侧为接口壳(对外 API、注册、事件),原生内核(C# 子节点自注册 `_cs_node`,或 C++ GDExtension 类)承担热循环;GDScript **必须保留可用的纯 GD 回退路径**(现有 `bullet_manager.gd` / `spatial_grid.gd` / `enemy_manager.gd``if not _cs_node:` 即此模式),确保内核未编译/未加载时游戏仍可运行。
- **改动后验证**GDScript 用 godot-mcp-pro `validate_script`;若真激活 C#/C++,须确保对应工程能编译(`.csproj` / GDExtension 构建)、过 `ADR-L1` review,并补基准对比(迁移前后 `_physics_process` 耗时)证明确有净收益,否则不合入 —— **性能优化以数据论成败,不以语言层级论高低**