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

86 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)