Files
XCEngine/docs/api/XCEngine/Scripting/Mono/Mono.md

4.5 KiB
Raw Blame History

Mono

命名空间: XCEngine::Scripting

类型: submodule

描述: 收纳基于 Mono 的托管脚本运行时实现,以及当前脚本字段发现、默认值读取与运行时桥接的 Mono 后端文档。

概览

docs/api/XCEngine/Scripting/Mono 对应 engine/include/XCEngine/Scripting/Mono。它不是独立命名空间,而是脚本系统按运行时后端拆出的子目录。

当前这里的核心公开类型是 MonoScriptRuntime。它负责:

  • 初始化 Mono root domain / app domain
  • 加载 XCEngine.ScriptCore.dllGameScripts.dll
  • 发现应用程序集中的非抽象 MonoBehaviour 子类
  • 构建字段元数据与字段默认值查询能力
  • 创建脚本实例、注入上下文并调用生命周期
  • 通过 internal call 把 DebugTimeInputGameObjectTransform 等托管 API 接回原生实现

字段发现模型

这一轮最重要的语义更新,是当前 Mono 后端已经不再等同于“只发现 public 实例字段”。

当前真实规则是:

  • 先排除:
    • static
    • const / literal
    • readonly / init-only
  • 然后接受:
    • public 实例字段
    • 或者标了 [SerializeField]private 实例字段
  • 最后再按当前支持的字段类型过滤

这意味着:

  • FieldMetadataProbe.Health / Speed / Label 这类 public 字段会被发现
  • FieldMetadataProbe.HiddenFlag 这种 [SerializeField] private 字段也会被发现
  • IgnoredPrivateCounter 这类未标注的 private 字段仍然会被忽略
  • SharedCounter 这类 static 字段会被忽略
  • UnsupportedRotation 这类当前不支持的类型也会被忽略

为什么要支持 [SerializeField] private

这是一个很典型的 Unity 风格设计,但它的价值不只是“和 Unity 看齐”。更重要的是它解决了工程里的两个真实矛盾:

  • 你希望字段能被序列化、能被 Inspector 或场景存储驱动
  • 但你又不希望把这个字段暴露成脚本类的公开 API

支持 [SerializeField] private 的好处包括:

  • 保留封装边界,不必为了可持久化而把内部状态全部改成 public
  • 更适合后续重构,减少脚本 API 面被外部任意依赖
  • 和商业引擎用户的认知模型一致,降低脚本作者迁移成本

而继续忽略“未标注的 private 字段”,则能防止把纯运行时内部状态意外持久化。

为什么要排除 readonly

当前字段同步模型要求:

  • 运行前可以把 ScriptFieldStorage 里的覆盖值写回托管实例
  • 运行后可以把托管实例里的值同步回本地存储

readonly 字段不适合承担这类双向同步职责,因此当前实现把 init-only 直接排除。这是一个偏工程安全的取舍。

程序集事实来源

当前 Mono 后端的类发现和字段发现,依赖的是“编译后的程序集事实”,而不是直接扫描 .cs 源文件目录:

  • XCEngine.ScriptCore.dll 提供托管基类和属性
  • GameScripts.dll 提供应用层脚本类

这和商业引擎常见的做法一致Inspector 列表、字段元数据、默认值读取和真正实例化,都应该基于同一份已编译产物。

测试锚点

当前行为直接受到以下测试约束:

  • ClassFieldMetadataListsSupportedPublicInstanceFields
  • ClassFieldDefaultValueQueryReturnsSerializeFieldPrivateInitializers
  • SerializeFieldPrivateFieldsApplyStoredValuesAndPersistAcrossSceneRoundTrip
  • test_project_script_assembly.cpp 里的项目程序集发现与默认值读取用例

当前实现边界

  • 当前只有 Mono 后端,没有并列的 IL2CPP / Lua / 自研 VM 后端。
  • 字段发现只扫描应用程序集里的非抽象 MonoBehaviour 子类。
  • [SerializeField] private 已支持,但“未标注 private”“readonly”“不支持类型”仍然不会进入字段模型。
  • 项目脚本能否被发现,取决于它是否成功进入 GameScripts.dll,而不是取决于源文件是否存在于目录里。

头文件

相关指南

相关文档