docs: update scripting API docs

This commit is contained in:
2026-04-02 22:23:29 +08:00
parent ec2891b16b
commit 3f9e286637
25 changed files with 776 additions and 76 deletions

View File

@@ -8,15 +8,15 @@
## 概览
`docs/api/XCEngine/Scripting/Mono` 对应的是 `engine/include/XCEngine/Scripting/Mono` 子目录。它不是独立命名空间,而是脚本模块下按实现后端划分出的子目录。
`docs/api/XCEngine/Scripting/Mono` 对应 `engine/include/XCEngine/Scripting/Mono` 子目录。它不是独立命名空间,而是脚本模块按后端实现拆出来的子目录。
当前这里的核心类型只有 [MonoScriptRuntime](MonoScriptRuntime/MonoScriptRuntime.md)。它负责:
当前这里唯一的公开类型是 [MonoScriptRuntime](MonoScriptRuntime/MonoScriptRuntime.md)。它负责:
- 初始化 Mono root domain 和 app domain。
- 加载脚本核心程序集游戏程序集。
- 发现可用脚本类和公共实例字段
- `ScriptComponent``GameObject` 与托管对象实例绑定起来
- 在生命周期调用后把托管字段同步回本地存储
- 解析 `MonoScriptRuntime::Settings`,推导程序集目录、脚本核心程序集游戏程序集路径
- 加载 `XCEngine.ScriptCore.dll``GameScripts.dll`
- 发现应用程序集中的非抽象 `MonoBehaviour` 子类,并缓存支持的公共实例字段与生命周期方法
- `ScriptComponent` 创建托管实例,注入 `GameObject`/`ScriptComponent` 上下文,并在生命周期后同步字段
## 为什么单独分目录
@@ -25,11 +25,21 @@
- 可以清楚区分“脚本系统公共契约”和“具体后端实现”。
- 以后如果接入别的脚本后端,这里天然就是平行扩展点。
## 程序集来源
当前 Mono 后端既支持测试程序集目录,也支持项目资产脚本程序集目录:
- `managed/CMakeLists.txt` 会构建基础 `XCEngine.ScriptCore.dll``GameScripts.dll`
- 同一份 CMake 还会把 `project/Assets/**/*.cs` 编译到 `project/Library/ScriptAssemblies/GameScripts.dll`
- `tests/scripting/test_project_script_assembly.cpp` 已验证运行时能从项目程序集目录发现 `ProjectScripts.ProjectScriptProbe` 及其默认字段值。
## 当前实现边界
- 当前只实现了 Mono 后端,没有并列的 IL2CPP、Lua 或自研 VM 后端。
- 目录里只有一个 public header说明当前重点仍然是把单条托管脚本链路先跑顺
- 内部调用注册已覆盖 `GameObject``Transform``Camera``Light``MeshFilter``MeshRenderer` 和基础日志/时间桥接,但远不是完整编辑器级 API 面
- 目录里只有一个 public header说明当前重点仍然是把一条 Mono 托管链路跑通
- 类发现只扫描当前应用程序集里的非抽象 `MonoBehaviour` 子类,不会把工具类或抽象基类暴露给脚本绑定 UI
- 字段发现只接受受支持类型的 `public``static` 实例字段。
- internal call 目前已覆盖 `GameObject``Transform``Camera``Light``MeshFilter``MeshRenderer`、日志和时间桥接,但远不是完整编辑器级脚本 API 面。
## 头文件
@@ -38,6 +48,7 @@
## 相关指南
- [Scripting Runtime And Field Model](../../../_guides/Scripting/Scripting-Runtime-And-Field-Model.md)
- [Project Script Assembly And Field Sync](../../../_guides/Scripting/Project-Script-Assembly-And-Field-Sync.md)
## 相关文档

View File

@@ -6,7 +6,7 @@
**头文件**: `XCEngine/Scripting/Mono/MonoScriptRuntime.h`
**描述**: 基于 Mono 的脚本运行时实现,负责程序集加载、类发现、实例创建、internal call 桥接和生命周期调用。
**描述**: 基于 Mono 的脚本运行时实现,负责程序集解析与加载、类发现、实例创建、字段桥接、默认值读取和生命周期调用。
## 概览
@@ -14,10 +14,28 @@
- 初始化 root domain 和 app domain。
- 加载脚本核心程序集与游戏程序集。
- 发现继承 `MonoBehaviour` 的可用脚本类。
- 构建类元数据缓存
- 发现继承 `MonoBehaviour` 的可用脚本类,并缓存生命周期方法与字段元数据
- 读取类字段默认值
- 创建和销毁脚本实例。
- 通过 internal call 让托管脚本访问原生 `GameObject``Transform``Camera` 等能力。
- 把本地字段覆盖写入托管实例,并在生命周期之后同步回 `ScriptFieldStorage`
## Settings
构造函数接收一个 `Settings` 结构体,当前公开字段如下:
| 字段 | 说明 |
|------|------|
| `assemblyDirectory` | 程序集目录;若 `coreAssemblyPath` / `appAssemblyPath` 缺失,会以它为基准推导 DLL 路径。 |
| `corlibDirectory` | Mono 解析 `mscorlib.dll` 的目录;为空时会回退到 `assemblyDirectory``coreAssemblyPath` 所在目录。 |
| `coreAssemblyPath` | `XCEngine.ScriptCore.dll` 的完整路径。 |
| `appAssemblyPath` | `GameScripts.dll` 的完整路径。 |
| `coreAssemblyName` | 脚本核心程序集名,默认 `XCEngine.ScriptCore`。 |
| `appAssemblyName` | 应用程序集名,默认 `GameScripts`。 |
| `baseNamespace` | 核心托管 API 的命名空间,默认 `XCEngine`。 |
| `baseClassName` | 作为脚本基类查找入口的类型名,默认 `MonoBehaviour`。 |
`ResolveSettings()` 会在构造时和 `Initialize()` 前再次运行,补全目录和程序集路径。因此只提供 `assemblyDirectory` 也是合法用法;`tests/scripting/test_project_script_assembly.cpp` 就是基于项目脚本程序集目录这样初始化的。
## 生命周期
@@ -26,26 +44,22 @@
- [Shutdown](Shutdown.md) 会销毁 app domain、清空类缓存与实例缓存。
- 析构会自动调用 `Shutdown()`
## 常用访问器
- `IsInitialized()`
- `GetSettings()`
- `GetLastError()`
这些访问器主要用于测试、诊断和工具层。当前文档对 `GetLastError()` 单独补页,因为它直接关系到失败排查。
## 设计要点
- `ScriptEngine` 不直接碰 Mono API所有后端细节收敛在这个类里。
- 类缓存和实例缓存都基于稳定键,便于场景重建和脚本回绑。
- internal call 注册集中在本实现里,说明当前托管 API 面是围绕 Mono 后端组织的,而不是独立脚本 ABI
- 类缓存和实例缓存都基于稳定键,便于场景重建、类切换和实例回绑。
- `TryGetClassFieldDefaultValues()` 会创建一个临时托管对象并读取字段值,因此脚本字段默认值可以反映 C# 初始化表达式,而不只是原生零值
- `CreateScriptInstance()` 会先注入上下文 UUID再把本地存储里同名且类型匹配的字段写入托管实例。
- `SyncManagedFieldsToStorage()` 只回写本地已有字段,保证运行时不会把临时字段偷偷持久化到场景。
## 当前实现边界
- 当前只发现应用程序集中的非抽象 `MonoBehaviour` 子类。
- 支持的公共字段类型只覆盖 `float / double / bool / int32 / uint64 / string / Vector2 / Vector3 / Vector4 / GameObject`
- `SyncManagedFieldsToStorage()` 只会回写已经存在于 `ScriptFieldStorage` 中的字段;运行时临时字段不会自动持久化
- `OnRuntimeStop()` 只清理当前活动场景与实例,不会做完整 `Shutdown()`
- `TryGetAvailableScriptClasses()``GetScriptClassNames()` 和字段元数据查询都要求运行时已经初始化完成
- `SyncManagedFieldsToStorage()` 只会回写已经存在于 `ScriptFieldStorage` 中且类型仍匹配的字段;运行时临时字段不会自动持久化
- `OnRuntimeStop()` 只清理当前活动场景与实例,不会做完整 `Shutdown()`;程序集和类缓存会保留到显式 `Shutdown()` 或析构。
- 目前没有程序集热重载、增量编译监听或多场景并发运行支持。
## 公开方法
@@ -58,7 +72,11 @@
| [GetLastError](GetLastError.md) | 读取最近一次错误描述。 |
| [IsClassAvailable](IsClassAvailable.md) | 查询脚本类是否已发现。 |
| [GetScriptClassNames](GetScriptClassNames.md) | 返回已发现脚本类名列表。 |
| `IsInitialized()` | 判断运行时是否已完成初始化。 |
| `GetSettings()` | 返回已解析的运行时设置。 |
| [TryGetAvailableScriptClasses](TryGetAvailableScriptClasses.md) | 返回完整脚本类描述列表。 |
| [TryGetClassFieldMetadata](TryGetClassFieldMetadata.md) | 读取脚本类字段元数据。 |
| [TryGetClassFieldDefaultValues](TryGetClassFieldDefaultValues.md) | 读取脚本类字段默认值。 |
| [HasManagedInstance](HasManagedInstance.md) | 判断某脚本组件是否已有托管实例。 |
| [GetManagedInstanceCount](GetManagedInstanceCount.md) | 返回当前托管实例数。 |
| [GetManagedInstanceObject](GetManagedInstanceObject.md) | 读取托管对象裸指针。 |
@@ -77,6 +95,7 @@
- `engine/src/Scripting/Mono/MonoScriptRuntime.cpp`
- `tests/scripting/test_mono_script_runtime.cpp`
- `tests/scripting/test_project_script_assembly.cpp`
## 相关文档
@@ -84,3 +103,4 @@
- [IScriptRuntime](../../IScriptRuntime/IScriptRuntime.md)
- [ScriptEngine](../../ScriptEngine/ScriptEngine.md)
- [Scripting Runtime And Field Model](../../../../_guides/Scripting/Scripting-Runtime-And-Field-Model.md)
- [Project Script Assembly And Field Sync](../../../../_guides/Scripting/Project-Script-Assembly-And-Field-Sync.md)

View File

@@ -0,0 +1,32 @@
# MonoScriptRuntime::TryGetAvailableScriptClasses
**命名空间**: `XCEngine::Scripting`
**类型**: `method`
**头文件**: `XCEngine/Scripting/Mono/MonoScriptRuntime.h`
## 签名
```cpp
bool TryGetAvailableScriptClasses(
std::vector<ScriptClassDescriptor>& outClasses) const override;
```
## 当前实现流程
1. 清空 `outClasses`
2. 若运行时尚未初始化,直接返回 `false`
3. 遍历 `m_classes` 缓存,把每个条目转换成 `ScriptClassDescriptor`
4.`assemblyName -> namespaceName -> className` 排序后返回。
## 返回内容边界
- 只来自当前已发现的应用程序集脚本类缓存。
- 不包含抽象类。
- 不包含非 `MonoBehaviour` 子类。
## 相关文档
- [MonoScriptRuntime](MonoScriptRuntime.md)
- [ScriptEngine::TryGetAvailableScriptClasses](../../ScriptEngine/TryGetAvailableScriptClasses.md)

View File

@@ -0,0 +1,48 @@
# MonoScriptRuntime::TryGetClassFieldDefaultValues
**命名空间**: `XCEngine::Scripting`
**类型**: `method`
**头文件**: `XCEngine/Scripting/Mono/MonoScriptRuntime.h`
## 签名
```cpp
bool TryGetClassFieldDefaultValues(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className,
std::vector<ScriptFieldDefaultValue>& outFields) const override;
```
## 当前实现流程
1. 清空 `outFields`
2.`(assemblyName, namespaceName, className)` 查找缓存的类元数据。
3. 切换到当前 app domain。
4. 创建一个临时托管对象并执行默认构造。
5. 遍历缓存字段,逐个读取字段值并写入 `ScriptFieldDefaultValue`
6.`fieldName` 排序后返回。
## 为什么它重要
这里返回的是托管类真实初始化后的字段值,所以它能反映:
- C# 字段初始化表达式
- 默认构造后留下的初始状态
`ScriptEngine::TryGetScriptFieldModel()``ClearScriptFieldOverrides()` 都依赖这条数据。
## 失败路径
- 类找不到
- Mono 无法创建临时对象
- 任意字段读取失败
这些情况都会返回 `false`,并清空输出。
## 相关文档
- [TryGetClassFieldMetadata](TryGetClassFieldMetadata.md)
- [Project Script Assembly And Field Sync](../../../../_guides/Scripting/Project-Script-Assembly-And-Field-Sync.md)