Mana Engine:有关组件使用的更多信息

ECS体系结构之所以出色,是因为它在内存局部性方面表现非常出色,可以处理大量数据,并且可以将其自身用于多线程—这实际上只是在表面上体现了它的优势。 难怪Unity去年推出了新的ECS,Lumberyard也从他们从CryEngine继承的体系结构转移到了自己的ECS上。

但是有时您实际上并不想浏览大量数据。 有时候,在一千个对象中,您只想处理其中的几个。

我们在Mana Engine中发现的是,大多数时候您想对某些而非全部组件进行工作,它可以与以下三个事件之一联系在一起:创建,更新和销毁。

因此,Mana Engine支持知道何时新建,更改或更新组件。 许多代码库可能通过委托(即OnComponentCreated )或需要设置处理程序和各种样板代码的消息传递系统来执行此操作。


在Mana Engine中,每个有权访问给定组件类型的AuthorizedSystem本质上都可以访问已创建,更改或销毁的组件列表。 别大惊小怪。 没有设置。 没有钩子或处理程序。 没有回调。 只是已经提供给您的EntityIds哈希集。

它的工作原理如下:

 类MySystem:公共AuthorizedSystem  
{
无效Update()
{
//获取对所需组件的访问权限。
自动worldProxy = GetWorldProxy ();
//对于创建的集合中的每个ID ...
for(EntityId id:GetCreatedSet ())
{// ^魔术在这里发生。
//通过此ID查找所有组件。
EntityView ev = worldProxy.Get(id);
//如果它是一个完整的EntityView ...
如果(ev)
{
//对实体进行处理。
}
}
}
};

让我们分解一下代码。

  1. 我们拥有一个对MyComp1MyComp2都具有写入权限的AuthorizedSystem
  2. Update()调用中,我们获得两个组件的WorldProxy 。 我们想知道在此帧中是否有任何新创建的MyComp1组件,因此我们调用GetCreatedSet并对其进行迭代。
  3. 在循环内部,我们只想处理完整的 EntityView,这意味着它们存在所有组件。 因此,我们调用worldProxy.Get(id)并在使用前检查EntityView's完整性。 EntityView有一个布尔运算符,使我们可以使用语法if(ev)来检查其完整性。
  4. 最后,我们使用EntityView

您可以使用OnChangedSetOnDestroyedSet进行类似的操作。


Mana Engine自动执行所有簿记。 但是,有时记账可能很昂贵。 有时,您只想遍历任何内容(更改或未更改)。 在定义允许您这样做的组件时,有一个关键字。

其中一部分是一种哲学,即不为不需要的东西付钱,并具有在不妨碍自己的方式实现自己想要的东西的灵活性。

这是一个很好的例子。

在我们的测试游戏中,压力测试之一是渲染100k +个运动对象。 当我们这样做时,我们可以假设对象的每一帧在其变换数据中都会有一个新值。 我们不想保留这样的事实,即这些转换中有100k +发生了变化。 相反,我们只是假设它们会相应地更改和更新其GPU缓冲区。

但是,在另一种情况下,我们要渲染数千个静态对象和几个动态对象。 在这种情况下,静态对象几乎不会移动。 因此,我们只希望根据已知发生的更改来更新它们,而不是每帧更新它们的GPU缓冲区。


为此,我们有两个组成部分。 StaticModel和动态模型。 它们几乎相同,但是行为不同。

  struct StaticModel 
{
CHANGED_LIST_COMPONENT_DEFINITION();
矩阵变换
ModelId模型;
};构造DynamicModel
{
COMPONENT_DEFINITION();
矩阵变换
ModelId模型;
};

请注意, StaticModelCHANGED_LIST_COMPONENT_DEFINITION()标记,而DyanmicModelCOMPONENT_DEFINITION()标记。

更好的是,由于它成为组件上的constexpr static bool ,因此,如果您要访问不存在的更改集,我们可以使用if constexpr语句和static_asserts在编译时知道。 像这样:

 无效Update() 
{
自动wp = GetWorldProxy ();
for(自动&& id:GetChangedSet ())
{// ^编译时错误。
//用它做点什么...
}
}

请注意,名称“静态”和“动态”表示这些对象的变换数据,与它们的实际着色器或网格中的顶点无关。 StaticModel可以引用永无动画的对象,例如岩石。 它也可以指的是动画(通常)不会改变其位置,方向或比例,例如风中摇曳的树木。


在撰写本文时,检查创建,更改和销毁组件的能力还很原始。 我们计划扩展它,并使该语法更易于用于您经常需要在一起的更复杂的行为。

作为一个简单的示例,在上面的代码中,我们获得了CreatedSetMyComp1 ,但是我们更有可能想知道EntityView完成。 这意味着我们必须首先查看是否有任何新的MyComp1组件,然后检查是否有任何新的MyComp2组件,并且如果任何一个都有新的组件,请检查整个实体是否存在。 这就是很多我们可以打包和重复使用的样板代码。

诸如此类的核心功能是注定要成为Mana Engine的基础。 继续跟踪以了解是什么!