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:
+20
@@ -2,3 +2,23 @@
|
||||
.godot/
|
||||
/android/
|
||||
node_modules/
|
||||
|
||||
# --- C# / .NET (Godot Mono) ---
|
||||
# 主产物在 .godot/mono/ 下(已被 .godot/ 覆盖);以下为根级兜底
|
||||
/bin/
|
||||
/obj/
|
||||
*.user
|
||||
Rogue.csproj.old
|
||||
|
||||
# --- C++ GDExtension 构建中间产物 ---
|
||||
# 保留:源码、SConstruct、api dump、.gdextension、以及已构建的 debug .dll(开箱即用)
|
||||
# 忽略:编译中间产物与导入库。godot-cpp 子模块的 gen//bin/ 由其自带 .gitignore 处理。
|
||||
gdextension/**/*.obj
|
||||
gdextension/**/*.os
|
||||
gdextension/**/*.o
|
||||
gdextension/**/*.pdb
|
||||
gdextension/**/*.ilk
|
||||
gdextension/**/*.exp
|
||||
gdextension/**/*.lib
|
||||
**/.sconsign.dblite
|
||||
compile_commands.json
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "gdextension/godot-cpp"]
|
||||
path = gdextension/godot-cpp
|
||||
url = https://github.com/godotengine/godot-cpp
|
||||
branch = master
|
||||
shallow = true
|
||||
# godot-cpp 无 4.6/4.7 分支,用 master + 本工程 dump 的 4.7.1 extension_api.json(custom_api_file)精确匹配引擎。
|
||||
@@ -4,20 +4,20 @@
|
||||
|
||||
## 项目简介
|
||||
|
||||
Spellforge(万法熔炉)—— Godot 4.6 (Mono) 弹幕射击 Roguelite,SoA / ECS-Lite 架构,法术管道系统。工程概览见 [`README.md`](README.md)。
|
||||
Spellforge(万法熔炉)—— Godot 4.7.1-stable (Mono/.NET) 弹幕射击 Roguelite,SoA / 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` 耗时)证明确有净收益,否则不合入 —— **性能优化以数据论成败,不以语言层级论高低**。
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Godot.NET.Sdk/4.7.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<Nullable>disable</Nullable>
|
||||
<!-- 热路径逃生舱:C# 内核仅用于 profile 证明的密集数值循环(ADR-L1)。
|
||||
详见 CLAUDE.md「编程语言规范」。源码位于 csharp/,由 SDK 默认 **/*.cs glob 纳入编译。 -->
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -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 自动探测 MSVC(VS 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/ 覆盖旧文件
|
||||
```
|
||||
@@ -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 用 MSVC,SCons 自动探测 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
Binary file not shown.
@@ -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"
|
||||
Submodule
+1
Submodule gdextension/godot-cpp added at 5cec5bbd9c
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user