Mana Engine:使用组件

到目前为止,我的帖子一直侧重于系统及其安排方式。 另一半实际上是使用每个系统可以访问的组件。

我认为我没有明确说明它,但是如果您还没有从我以前的文章中弄清楚这一点,那么Mana Engine是一个实体组件系统(ECS)架构。 成为ECS对于使调度程序正常工作至关重要,因为系统会指示他们可以访问哪些组件。

顺便说一下,ECS的“实体”部分在Mana引擎中很小。 它有效地归结为所有组件都映射到的唯一整数ID。 如果两个组件映射到相同的ID,则它们在同一实体上。 没有实体类。 但是,有EntityViews。

EntityView基本上是给定实体的一部分。 因此,尽管一个实体可能绑定了十几个组件,但EntityView可能只对其中一些组件感兴趣。

World有点像所有实体中组件的整个集合。 实际上,组件以紧密排列的阵列存储在World上。 由于我们希望能够遍历组件的元组,因此World还需要Entity / EntityView具有的“视图”类似物。

输入WorldProxy 。 (我不太确定为什么我们不将其称为WorldView ,但是引擎仍在开发中,请耐心等待)。 World Proxies非常轻巧,基本上只是迭代EntityView的一种方法。


为了帮助说明这一点,这是我们需要处理的系统和相关组件的一个过于简单的示例。 它要做的就是通过重力常数向下移动对象。

首先,我们将声明要处理的组件类型。

 结构DynamicObject 
{
COMPONENT_DEFINITION(); //必须是一个组件。
Vector4位置;
//可能还有一些其他属性,但对于本示例而言并不重要。
};

现在,我们将创建一个时间单例。 其他一些系统将对此单例具有写访问权,并每帧更新其值。 由于我们的Gravity系统将具有const访问,因此我们知道它将在时间更新后运行。

 结构时间 
{
SINGLETON_COMPONENT_DEFINITION();
float deltaTime = 0.0f;
//许多其他与时间相关的属性。
};

这是系统本身。 我省略了一些名称空间之类的东西,以及其他一些对于此示例而言并非必不可少的方法。 我们关心的是Update函数。

 类Gravity:AuthorizedSystem  
{
//定义重力常数。
静态constexpr浮点重力重力= 9.8f;

void Update()覆盖最终版本
{
//抓紧时间部分。
时间和时间= GetSingleton ();

//创建一个可以访问所需组件的世界代理。
WorldProxy 代理= GetWorldProxy ();

// WorldProxy支持range-for。
// EntityViews是轻量级的,因此我们得到了副本。
for(EntityView 实体视图:代理)
{
//在我们的实体视图中获取指向组件的指针。
DynamicObject * pObj =实体视图.GetMutable ();

//施加重力。
pObj-> position + = Vector4 :: Down * gravityAmount * time.deltatTime;
}
}
};

我已经很好地评论了。 但是这里有一些有趣的“元”要指出的事情。

第一,代码非常简单。 抓取您的数据。 对它们进行工作。 无需处理深层次结构,甚至无需考虑游戏中的其余代码。 您可以仅将这个系统作为原子单元来关注。

第二,即使我们尚未编写处理任何多线程概念的代码,该代码也将与游戏中的许多其他系统并行运行,并保证线程安全。 因此,所有技能水平的程序员都可以使用此引擎非常有效。 作为奖励,Mana Engine具有一些并行功能,如果成为瓶颈,可以使该系统扩展。 我将在以后的文章中讨论。

第三,如果您搞砸了,编译器将阻止您。 注意我必须使用函数GetMutable<DynamicObject()因为我想要可变的访问。 如果系统仅具有对该组件的const访问权限,并且我使用了GetMutable() ,则由于WorldProxy内部存在static_assert ,因此它甚至都不会编译。

为了说明起见,下面是相同的代码,但没有乱七八糟的注释,并且使用了一些我不想妨碍示例的其他功能:

 类Gravity:AuthorizedSystem  
{
静态constexpr浮点重力重力= 9.8f;

void Update()覆盖最终版本
{
时间和时间= GetSingleton ();
for(auto [id,pObj]:GetEntities ())
{
pObj-> position + = Vector4 :: Down * gravityAmount * time.deltatTime;
}
}
};

相当小而直接。


上面是一个简单的情况,通常系统可能会更复杂。 但是,我们发现,当您将事情分解成几个系统时,引擎工作得最好,每个系统都尽可能地简化工作。

其原因是由于引擎试图并行化系统。 当每个任务很小时,它可以最好地完成其设计目标。 当每个系统仅做一件事情时,就更容易推断每个系统在做什么。 因此,实际上上述代码与我们实际使用的代码并不遥远。


与往常一样,请随时澄清。 您可以在Twitter(@ tloch14)上给我发消息或在此处发表评论。 我很乐意回答有关细节的任何问题或疑问。