Skip to content
云风 edited this page Jan 22, 2024 · 3 revisions

场景管理

Ant 使用 ECS 结构管理对象。所有的对象 (entity) 都是平坦的放在 world 中。

游戏对应的虚拟世界,我们称之为场景 (scene) 。场景上的每个物件都有它在场景中的空间状态,我们把这些有空间状态的物件称之为场景对象,会拥有一个 scene 组件。

不是所有的场景对象都会在视觉上呈现出来,只有那些拥有模型 (mesh) 组件和材质 (material) 组件的 entity 才可以被引擎渲染。通常,每个有 mesh 组件的 entity 都有 scene 组件。因为没有空间状态无法被渲染。

注:目前有一个特例。带蒙皮的 mesh 在同一个 entity 上没有 scene 组件。这是因为,可以有多个带蒙皮的 mesh 共享一个场景对象。因为 ecs 的 world 中,entity 是有序排列的,我们会取排在蒙皮组件前面的紧靠着它的 scene 逐渐作为它的空间状态。

Ant 用森林结构组织场景对象。Entity 虽然平坦,但其中的 scene 组件却有依附关系。scene 组件被组织在一棵棵场景树上,每个 scene 组件都记录了自己的父亲的 entity id 。0 是保留的无效 id ,当父亲为 0 时,它是根节点。

和许多其它引擎不同,Ant 中 scene 的数据结构更为简单。它是一个固定的 C 组件,仅仅记录了自身空间状态(以 SRT 的形式)和父节点的 id 。它没有对子节点的反向引用。

限制:引擎要求,所有的父节点必须在子节点之前创建出来,即包含父亲 scene 组件的 entity 在 ecs 的 world 中必须排列在包含子 scene 组件的 entity 的前面。如果你依次创建场景对象,这通常不会成为限制,因为当你没有创建出父节点的话,创建子节点时就无法指定父亲的 entity id 。但如果你需要运行时修改一个场景对象的父亲,就要注意不要修改为指向比它后创建出来的对象。

创建场景对象

在创建 entity 时实例化 scene 组件,该 entity 就成为了一个场景对象。通常是用 Prefab 实例化。

修改场景对象的空间状态

游戏,一般的需求就是创建若干场景对象,然后修改它们的位置。这一小节简单介绍一下这个需求怎样实现。

scene 组件记录了场景对象的缩放量、旋转量(一个四元数)、位移量(空间位置),以及正方向(默认指向 Z 轴正方向)。固然我们可以直接修改 scene 组件里面的对应变量,但是,场景对象的空间状态信息最终会受它的父亲乃至祖先的影响。光改变单个场景对象中的状态,无法立刻反应到它的所有子孙上。

引擎并不会在每帧全部重新计算所有场景对象最终的世界矩阵,因为,如果你的场景对象数量特别巨大的话,每帧计算有很大的开销。所以,我们需要给当帧改变过 scene 组件内部状态的 entity 标记上一个 ECS 的 tag : scene_needchange 。引擎只会在当前帧渲染前处理标记有这个 tag 的场景对象以及它们的子孙。

使用 ecs 固然可以直接操作 component 数据,但使用者可能会遗漏打 tag 的步骤。所以我们建议使用封装过的函数。

local iom = ecs.require "ant.objcontroller|obj_motion"

使用 ant.objcontroller 包里的 obj_motion 子模块可以使用这些封装过以上细节的 api ,用来修改一个带有场景对象的 entity 的空间状态。因为随着引擎的开发,这些 api 可能被调整。本文不列出具体 api 列表,可以参考 /pkg/ant.objcontroller/obj_motion.lua 中的具体实现。

修改场景对象的父节点

一般在 entity 创建时指定它的父节点 id 。暂时没有封装在运行时修改它的 api ,但你可以直接修改 scene.parent 修改它,然后记得给 entity 打上 scene_needchange 的 tag 。

world:create_instance() 实例化 Prefab 得到的 instance ,默认是没有父亲的。但你可以用

world:instance_set_parent(instance, parent)

指定它的父亲。这个 api 将把这个预制件中所有没有指定父亲的根集合中所有 entity 的父节点都设置为 parent 。这个 api 允许在运行时被调用多次,用来更换父节点。

场景对象的销毁

我们在介绍 Prefab 时介绍了如果删除 Entity 或一组 entity 实例。如果所删除的 entity 是一个场景对象,即包含有 scene 组件,那么它的所有子孙都会被删除。

Clone this wiki locally