Files
XCEngine/docs/api/XCEngine/Scene/Scene/Scene.md

7.6 KiB
Raw Blame History

Scene

命名空间: XCEngine::Components

类型: class

头文件: XCEngine/Scene/Scene.h

描述: 当前对象树的场景级容器,负责 GameObject 所有权、根节点入口、场景更新与完整树结构的保存/加载。

设计定位

商业级游戏引擎通常不会让“对象本身”同时承担对象集合管理、生命周期入口和场景存档边界。Scene 在这里承担的就是这层容器职责:

  • 统一持有对象所有权
  • 提供根对象遍历入口
  • 驱动场景级 Update / FixedUpdate / LateUpdate
  • 作为整棵对象树的序列化和反序列化边界

这样拆分的好处是:GameObject 可以专注于对象自身与子树,Scene 则负责“这一整片运行时世界如何被管理和保存”。

当前真实职责

Scene 内部当前维护四组关键状态:

  • m_gameObjects
    • 以对象 ID 为键保存 std::unique_ptr<GameObject>
    • 是场景内对象的唯一所有权容器
  • m_rootGameObjects
    • 保存所有根对象的 ID
    • 决定场景遍历和序列化的根入口顺序
  • m_gameObjectIDs
    • 保存当前场景内对象 ID 集合
    • 用于快速判断成员关系
  • 事件入口

这种“对象表 + 根列表”的拆法很典型,因为“按 ID 找对象”和“从根入口递归整棵树”是两种不同的使用需求。

创建与销毁路径

CreateGameObject

CreateGameObject 是当前标准运行时创建入口。它会完成:

  • 分配并持有新的 GameObject
  • 把对象注册到全局 GameObject::GetGlobalRegistry()
  • 写入当前场景指针
  • 挂到根节点列表或父对象之下
  • 立刻调用 Awake()
  • 触发 OnGameObjectCreated

这条路径产生的对象最符合“真实运行时场景对象”的语义。

DestroyGameObject

DestroyGameObject 会:

  • 先递归销毁子对象
  • 再把对象从父节点子列表或根列表里移除
  • 触发 OnGameObjectDestroyed
  • 调用对象的 OnDestroy()
  • 从场景容器和全局 registry 中删除

事件与 OnDestroy() 的顺序很重要:当前实现是先发场景销毁事件,再让对象给普通组件分发 OnDestroy()

Destructor

Destructor 当前不会重放上述显式销毁链。它只会:

  • 从全局 registry 擦除属于本场景的对象
  • 清空内部 unique_ptr 容器

所以 ~Scene() 不是“完整生命周期清理器”,而只是容器析构。

场景更新语义

UpdateFixedUpdateLateUpdate 当前都从根对象开始遍历。

按当前实现:

  • Update() 会先调用每个激活根对象的 Start(),再调用 Update(deltaTime)
  • FixedUpdate()LateUpdate() 也只会继续驱动 IsActiveInHierarchy() 为真的对象
  • Scene::m_active 当前不会作为这些接口的门控条件

因此更准确的理解方式是:

  • 对象级是否真正参与运行,看 GameObject::IsActiveInHierarchy()
  • Scene::IsActive() 目前更像一个可保存的场景状态位,而不是运行时强门控

Tag / Layer 与场景查询

FindGameObjectWithTag 当前已经是真正的 tag 查询:

  • 从根对象开始
  • 对每个对象调用 CompareTag(tag)
  • 按深度优先顺序返回第一个匹配项

场景保存与恢复也已经覆盖 tag / layer

这意味着场景级 tag 查询、原生 GameObject 字段,以及托管 GameObject.tag / layer 当前共享同一份底层状态。tests/Scripting/test_mono_script_runtime.cpp 中的 GameObjectTagAndLayerApiExposeUnityStylePropertiesAndCompareTag 就验证了脚本侧写回 tag 后,原生场景查询会立即看到结果。

序列化与反序列化模型

SerializeToString

SerializeToString 输出完整场景文本,包含:

  • 场景名与场景 active 位
  • 每个对象的 iduuidnametagactivelayerparenttransform
  • 每个普通组件的 component=<type>;payload

对象按“根列表顺序 + 子树深度优先顺序”输出。

DeserializeFromString

DeserializeFromString 采用两阶段重建:

  1. 先解析所有对象块与组件 payload
  2. 再创建全部对象、恢复字段、挂组件、连接父子关系

这条路径当前不会经过 CreateGameObject(),因此不会自动触发:

  • OnGameObjectCreated
  • Awake()
  • Start()

这是当前场景创建和场景加载之间最重要的行为差异之一。

当前实现限制

  • m_active 当前不会阻止场景更新
  • 场景析构不会为所有对象补发显式销毁链
  • 反序列化不会触发创建事件,也不会补发 Awake() / Start()
  • 组件恢复依赖 ComponentFactoryRegistry
  • Save / Load 的错误处理目前比较轻,不提供事务式恢复
  • SetName 不会同步更新 SceneManager 里可能存在的管理 key

推荐使用方式

  1. 运行时对象创建优先使用 CreateGameObject
  2. 需要当前场景内“第一个匹配对象”时,用 FindGameObjectWithTag;需要全局扫描时,再考虑 GameObject::FindGameObjectsWithTag()
  3. 需要保存完整对象树时,使用 SerializeToString / DeserializeFromString
  4. 从文本反序列化恢复后,如果某些系统依赖 Awake() 侧初始化,要显式设计补发或重建流程,不要假设引擎已经自动补齐。

公开方法

方法 说明
Constructor 构造空场景。
Destructor 销毁场景容器。
GetName 查询场景名称。
SetName 设置场景名称。
IsActive 查询场景 active 标志。
SetActive 设置场景 active 标志。
CreateGameObject 创建场景对象并注册到场景。
DestroyGameObject 从当前场景树中销毁对象。
Find 按名称查找对象。
FindByID 按 ID 查找对象。
FindGameObjectWithTag 按真实 tag 深度优先查找第一个匹配对象。
FindObjectOfType 查找第一个匹配组件。
FindObjectsOfType 查找所有匹配组件。
GetRootGameObjects 获取根对象列表。
Update 驱动每帧更新。
FixedUpdate 驱动固定步长更新。
LateUpdate 驱动晚更新。
Save 保存到文件。
Load 从文件加载。
SerializeToString 序列化完整场景文本。
DeserializeFromString 从完整场景文本重建场景。
OnGameObjectCreated 访问对象创建事件。
OnGameObjectDestroyed 访问对象销毁事件。

相关文档