docs: sync api and planning docs
This commit is contained in:
@@ -10,29 +10,56 @@
|
||||
|
||||
## 概览
|
||||
|
||||
`ScriptEngine` 是当前脚本模块里最重要的原生协调者。它自己不执行脚本字节码,也不保存场景数据,而是负责把下面几层真正串起来:
|
||||
`ScriptEngine` 是当前脚本模块里最重要的原生协调者。它自己不执行脚本字节码,也不保存场景资产,而是把下面几层真正串起来:
|
||||
|
||||
- `SceneRuntime` 提供“场景开始运行/停止运行/每帧更新”的入口。
|
||||
- `SceneRuntime` 提供“场景开始运行 / 停止运行 / 每帧更新”的入口。
|
||||
- `ScriptComponent` 提供脚本绑定与字段缓存。
|
||||
- `IScriptRuntime` 提供具体脚本后端能力。
|
||||
|
||||
这也是商业引擎里最常见的做法之一:生命周期顺序和场景级追踪必须掌握在原生调度器手里,不能散落在各个后端实现里,否则脚本后端一换,Play 模式语义就会漂。
|
||||
|
||||
当前它的核心职责包括:
|
||||
|
||||
- 在运行时开始时收集场景中的脚本组件并建立追踪表。
|
||||
- 订阅 `Scene::OnGameObjectCreated()`,把运行中创建的新对象也纳入脚本追踪。
|
||||
- 保存运行时固定步长配置,并把它暴露给托管时间查询接口。
|
||||
- 根据对象激活状态、组件启用状态和脚本类绑定状态决定脚本是否应当运行。
|
||||
- 按顺序创建实例、调用 `Awake / OnEnable / Start / Update...`。
|
||||
- 按顺序创建实例、调用 `Awake / OnEnable / Start / FixedUpdate / Update / LateUpdate`。
|
||||
- 在运行时字段、本地字段缓存、类元数据和类默认值之间建立一致的读取模型。
|
||||
- 为编辑器/Inspector 提供脚本类列表、字段模型、批量写入和清除覆盖能力。
|
||||
- 为编辑器 / Inspector 提供脚本类列表、字段模型、批量写入和清除覆盖能力。
|
||||
|
||||
## 生命周期
|
||||
## 生命周期主控
|
||||
|
||||
- [Get](Get.md) 返回进程级单例。
|
||||
- 默认运行时是内部持有的 `NullScriptRuntime`。
|
||||
- [SetRuntime](SetRuntime.md) 可替换具体后端;传 `nullptr` 时回退到空运行时。
|
||||
- [OnRuntimeStart](OnRuntimeStart.md) / [OnRuntimeStop](OnRuntimeStop.md) 管理整条脚本运行链路。
|
||||
- [OnRuntimeStart](OnRuntimeStart.md) 启动整条脚本运行链路。
|
||||
- [OnRuntimeStop](OnRuntimeStop.md) 停止运行、销毁活体实例并清理跟踪表。
|
||||
|
||||
## 内部追踪模型
|
||||
`OnRuntimeStart()` 的当前流程有两个很重要的实现细节:
|
||||
|
||||
- 它会先暂存当前 fixed delta 配置,再内部调用一次 `OnRuntimeStop()` 清旧状态,然后把配置写回去。
|
||||
- 它会在通知运行时启动后,再递归收集场景中的脚本组件,并订阅 `Scene::OnGameObjectCreated()`。
|
||||
|
||||
这让“切场景 / 重启 Play 模式 / 运行中生成对象”的脚本生命周期都收口到一条统一流程里。
|
||||
|
||||
## 固定步长配置与托管时间
|
||||
|
||||
`ScriptEngine` 当前把“固定步长配置值”和“某次 fixed tick 的实际回调参数”分成了两层:
|
||||
|
||||
- [SetRuntimeFixedDeltaTime](SetRuntimeFixedDeltaTime.md) 写入的是引擎保存的配置值。
|
||||
- [OnFixedUpdate](OnFixedUpdate.md) 使用的是调用方本次传入的 `fixedDeltaTime`。
|
||||
|
||||
通常这两者应该保持一致,但当前代码不会自动强制它们一致。
|
||||
|
||||
另外还要注意两点:
|
||||
|
||||
- [OnRuntimeStart](OnRuntimeStart.md) 会在内部 stop/start 之间保留启动前配置的 fixed delta。
|
||||
- [OnRuntimeStop](OnRuntimeStop.md) 完成后会把配置重置回 [DefaultFixedDeltaTime](DefaultFixedDeltaTime.md)。
|
||||
|
||||
Mono 侧 `Time.fixedDeltaTime` 当前直接读取 [GetRuntimeFixedDeltaTime](GetRuntimeFixedDeltaTime.md),而 `Time.deltaTime` 则来自每次生命周期回调传入的 delta。因此脚本里这两个时间值虽然名字相近,但当前来源并不相同。
|
||||
|
||||
## 运行时追踪模型
|
||||
|
||||
当前 `ScriptEngine` 通过 `(gameObjectUUID, scriptComponentUUID)` 唯一标识一个脚本实例状态,并保存:
|
||||
|
||||
@@ -43,9 +70,34 @@
|
||||
- `startCalled`
|
||||
- `startPending`
|
||||
|
||||
这说明当前生命周期状态是显式状态机,而不是每次调用都从托管世界反查。
|
||||
这说明当前生命周期状态是显式状态机,而不是每次调用都从托管世界反查。好处是:
|
||||
|
||||
## 脚本类与字段模型
|
||||
- 生命周期顺序可控。
|
||||
- 运行中增删对象时更容易保持稳定顺序。
|
||||
- 后端只需要关心“如何创建 / 销毁 / 调方法”,而不需要再自己维护一份场景状态机。
|
||||
|
||||
`ShouldScriptRun()` 当前还明确要求以下条件同时成立:
|
||||
|
||||
- 运行时已经启动。
|
||||
- `state.context.scene == m_runtimeScene`。
|
||||
- 场景处于激活状态。
|
||||
- `GameObject` 处于 hierarchy active。
|
||||
- `ScriptComponent` 已启用。
|
||||
- `ScriptComponent` 当前仍有脚本类绑定。
|
||||
|
||||
## 生命周期调用与字段回写
|
||||
|
||||
`ScriptEngine` 不是只做“调方法”这么简单。当前 `InvokeLifecycleMethod()` 在每次调用运行时生命周期方法后,都会立即调用 `SyncManagedFieldsToStorage()`。
|
||||
|
||||
这意味着:
|
||||
|
||||
- 字段模型中的活体值和本地存储值会在生命周期边界上持续靠拢。
|
||||
- Inspector 或调试工具不需要等到运行时结束,才有机会看到脚本侧最新写回的字段。
|
||||
- 当前回写策略仍然是保守的,只同步本地已存在且类型匹配的字段。
|
||||
|
||||
这也是为什么 `ScriptEngine` 既是生命周期总调度器,也是脚本字段数据服务入口。
|
||||
|
||||
## 脚本类发现与重绑定
|
||||
|
||||
除了生命周期调度,`ScriptEngine` 还负责把运行时暴露成编辑器可消费的数据接口:
|
||||
|
||||
@@ -54,7 +106,13 @@
|
||||
- `ApplyScriptFieldWrites()` 会逐项返回 `Applied / UnknownField / TypeMismatch / StoredOnlyField` 等状态。
|
||||
- `ClearScriptFieldOverrides()` 会把声明字段重置回类默认值,并删除本地存储中的覆盖项。
|
||||
|
||||
这也是当前 Inspector 和脚本字段编辑工具最该依赖的 API 面。
|
||||
类重绑定链路也收口在这里:
|
||||
|
||||
- `ScriptComponent::SetScriptClass()` / `ClearScriptClass()` 先更新组件上的绑定字符串。
|
||||
- 然后由 `ScriptEngine::OnScriptComponentClassChanged()` 负责停掉旧实例、移除旧跟踪、按新绑定重新跟踪。
|
||||
- 如果新绑定满足运行条件,会立即尝试 `EnsureScriptReady()`,也就是创建实例并补发 `Awake / OnEnable`。
|
||||
|
||||
需要注意的是,`OnScriptComponentClassChanged()` 并不会回滚 `ScriptComponent` 上已经写入的新绑定。如果新类在运行时里不存在,或 `CreateScriptInstance()` 失败,组件仍然保持新绑定,只是当前没有活体实例。
|
||||
|
||||
## 线程语义
|
||||
|
||||
@@ -66,6 +124,7 @@
|
||||
|
||||
- 只跟踪当前运行场景里的脚本组件。
|
||||
- `Start` 生命周期会在第一次 `OnUpdate()` 前补发一次,而不是在 `OnRuntimeStart()` 里立即调用。
|
||||
- 当前不会自动把 [SetRuntimeFixedDeltaTime](SetRuntimeFixedDeltaTime.md) 的配置值与 [OnFixedUpdate](OnFixedUpdate.md) 的参数强制同步;时间系统调用方需要自己保持一致。
|
||||
- `TrySetScriptFieldValue()` 只有在后端能返回类字段元数据时,才会强校验字段名和类型;否则会退回到纯本地存储写入。
|
||||
- `TryGetScriptFieldModel()` 会优先使用运行时返回的类默认值,而不是简单地把每种类型置零。
|
||||
- `TryGetScriptFieldSnapshots()` 在模型成功但字段为空时会返回 `false`,调用方不能把“返回 `false`”简单等同于“接口失败”。
|
||||
@@ -73,17 +132,26 @@
|
||||
## 常用访问器
|
||||
|
||||
- `GetRuntime()`
|
||||
- `GetRuntimeFixedDeltaTime()`
|
||||
- `IsRuntimeRunning()`
|
||||
- `GetRuntimeScene()`
|
||||
|
||||
这些是简单内联访问器,当前文档重点放在影响状态机和运行行为的核心方法上。
|
||||
|
||||
## 公开常量
|
||||
|
||||
| 常量 | 说明 |
|
||||
|------|------|
|
||||
| [DefaultFixedDeltaTime](DefaultFixedDeltaTime.md) | 默认运行时固定步长配置,当前为 `1/50` 秒。 |
|
||||
|
||||
## 公开方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| [Get](Get.md) | 获取全局脚本引擎实例。 |
|
||||
| [SetRuntime](SetRuntime.md) | 设置具体脚本后端。 |
|
||||
| [SetRuntimeFixedDeltaTime](SetRuntimeFixedDeltaTime.md) | 设置运行时固定步长配置。 |
|
||||
| [GetRuntimeFixedDeltaTime](GetRuntimeFixedDeltaTime.md) | 读取当前运行时固定步长配置。 |
|
||||
| [OnRuntimeStart](OnRuntimeStart.md) | 启动脚本运行时。 |
|
||||
| [OnRuntimeStop](OnRuntimeStop.md) | 停止脚本运行时。 |
|
||||
| [OnFixedUpdate](OnFixedUpdate.md) | 驱动固定步长脚本更新。 |
|
||||
@@ -107,9 +175,9 @@
|
||||
## 真实行为依据
|
||||
|
||||
- `engine/src/Scripting/ScriptEngine.cpp`
|
||||
- `tests/scripting/test_script_engine.cpp`
|
||||
- `tests/Scripting/test_script_engine.cpp`
|
||||
- `tests/Scene/test_scene_runtime.cpp`
|
||||
- `tests/scripting/test_mono_script_runtime.cpp`
|
||||
- `tests/Scripting/test_mono_script_runtime.cpp`
|
||||
|
||||
## 相关文档
|
||||
|
||||
|
||||
Reference in New Issue
Block a user