-
Notifications
You must be signed in to change notification settings - Fork 385
Scene
Ant 使用 ECS 结构管理对象。所有的对象 (entity) 都是平坦的放在 world 中。
游戏对应的虚拟世界,我们称之为场景 (scene) 。场景上的每个物件都有它在场景中的空间状态,我们把这些有空间状态的物件称之为场景对象,会拥有一个 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 组件,那么它的所有子孙都会被删除。