Files
XCEngine/docs/api/_guides/Scene/Scene-Lifecycle-And-Serialization.md

86 lines
3.8 KiB
Markdown
Raw Normal View History

2026-03-26 19:34:24 +08:00
# Scene Lifecycle And Serialization
## 为什么场景系统通常这样分层
商业级游戏引擎很少把“场景”“对象”“组件”揉成一个类。更常见的拆法是:
- 场景负责对象集合、根节点入口和存档边界。
- 对象负责层级关系与生命周期转发。
- 组件负责真正的可组合行为。
`XCEngine` 当前的 `Scene` / `GameObject` / `Component` 关系基本就是这个思路,所以它看上去会比较像 Unity 风格的设计,而不是传统单一 Entity 容器。
## `Scene` 真正负责什么
当前 `Scene` 最核心的职责有三件事:
1. 持有 `GameObject` 的唯一所有权。
2. 从根对象入口驱动 `Update` / `FixedUpdate` / `LateUpdate`
3. 作为保存和加载的边界,把一整棵对象树序列化出去或重新建回来。
这样做的好处是,场景可以被理解成“一个可整体保存、整体切换的运行时世界片段”。
## 为什么要有根对象列表
场景里既有对象树,又单独保存 `m_rootGameObjects`,这不是重复设计,而是为了让两件事都变简单:
- 查“从哪里开始遍历场景”时,不用扫描全体对象找没有父节点的项。
- 对象挂接和脱离父节点时,只要同步更新根列表即可。
这是很多商用引擎场景树都会采用的做法,因为“根入口”和“局部子树”是两种不同维度的查询。
## 当前 active 状态有两层,但还没有完全打通
理解这块很重要:
- `SceneManager::GetActiveScene()` 是“哪一个场景被管理器认为是当前活动场景”。
- `Scene::IsActive()``Scene` 自己保存的一个布尔标志。
- `GameObject::IsActiveInHierarchy()` 才是对象更新真正参与的门槛。
在当前实现里,这三者并没有完全联动:
- `Scene::SetActive(false)` 不会阻止 `Scene::Update()`
- `SceneManager::SetActiveScene()` 也不会自动改 `Scene::IsActive()`
这意味着现在更稳妥的理解方式是:
- `SceneManager` 的 active scene 更接近“当前选中的场景引用”。
- `Scene::m_active` 更接近“一个可保存的场景状态位”。
## 序列化为什么用自定义文本格式
当前 `Scene::SerializeToString()` 没有走 JSON而是用了非常直接的文本块格式。这样做的现实好处是
- 实现简单。
- 组件可以继续用自己的 `Serialize(std::ostream&)` 输出。
- 调试时直接打开文本就能看见对象、父子关系和组件条目。
但代价也很明确:
- 格式是引擎私有的,没有版本协商。
- 解析鲁棒性有限。
- 组件恢复依赖 `ComponentFactoryRegistry`,组件名或注册表变化都会影响加载结果。
## 当前最需要小心的几个事实
- 场景析构不会自动为所有对象走显式销毁流程,所以不要把 `~Scene()` 当成完整生命周期清理器。
- `FindGameObjectWithTag()` 现在其实是在按名字查。
- `LoadSceneAsync()` 现在不是异步,更多只是一个名字上预留好的 API。
- `LoadScene()` 后的管理器 key 来自文件名,而不是场景内部保存的 `scene=` 名称。
## 实际使用建议
如果你现在基于这套系统开发,比较稳妥的做法通常是:
- 需要 `OnDestroy` 或场景事件时,显式调用对象销毁入口,不要只依赖场景析构。
- 需要稳定查场景时,优先用 `CreateScene()` 的名称或加载文件的文件名 stem不要假设一定等于场景内部名字。
- 不要把 `LoadSceneAsync()` 当成真正的后台加载接口。
- 不要把 `FindGameObjectWithTag()` 当成完整 tag 系统。
## 相关 API
- [Scene Module](../../XCEngine/Scene/Scene.md)
- [Scene](../../XCEngine/Scene/Scene/Scene.md)
- [SceneManager](../../XCEngine/Scene/SceneManager/SceneManager.md)
- [GameObject](../../XCEngine/Components/GameObject/GameObject.md)