组件实体系统-更新和呼叫订单


10

为了使组件能够更新每一帧(并使此功能不包含在不需要的组件中),我想到了制作一个UpdateComponent组件的想法。诸如MovableComponent(保持速度)的其他组件将从IUpdatable抽象类继承。这迫使MovableComponent实现一个Update(gametime dt)方法,而另一个RegisterWithUpdater()实现提供UpdateComponent了指向的指针MovableComponent。许多组件可以执行此操作,然后UpdateComponent可以调用其所有Update(gametime dt)方法,而不必关心它们是谁或什么。

我的问题是:

  1. 这看起来像是正常的东西还是任何人使用的东西?我在这个问题上找不到任何东西。
  2. 我该如何维护物理等组件的顺序,然后更改位置?这有必要吗?
  3. 还有什么其他方法可以确保应处理的每一帧实际上都得到处理?

编辑
我想我将考虑如何为实体管理器提供可更新的类型列表。然后,该类型的所有组件都可以更新而不是针对每个实体进行管理(无论如何,它们只是我系统中的索引)。

仍然。我的问题对我仍然有效。我不知道这是否是理性/正常的,还是其他人倾向于这样做。

另外,失眠症患者也很棒! /编辑

为前面的示例精简代码:

class IUpdatable
{
public:
    virtual void Update(float dt) = 0;
protected:
    virtual void RegisterAsUpdatable() = 0;
};

class Component
{
    ...
};

class MovableComponent: public Component, public IUpdatable
{
public:
    ...
    virtual void Update(float dt);
private:
    ...
    virtual void RegisterWithUpdater();
};

class UpdateComponent: public Component
{
public:
    ...
    void UpdateAll();
    void RegisterUpdatable(Component* component);
    void RemoveUpdatable(Component* component);
private:
    ...
    std::set<Component*> updatables_;
};

您有一个不可更新组件的示例吗?这似乎毫无用处。将更新功能作为虚拟功能放入组件中。然后只需更新实体中的所有组件,无需“ UpdateComponent”
Maik Semder 2011年

一些组件更像是数据持有者。类似于PositionComponent。许多对象可以具有位置,但是如果它们是固定的(可能占多数),则所有这些额外的虚拟调用都可以建立。或者说一个HealthComponent。不需要在每一帧都做任何事情,只需在需要时进行修改即可。也许开销还算不错,但是我的UpdateComponent试图在每个组件中都没有Update()。
ptpaterson 2011年

5
根据定义,仅保存数据且没有随时间更改数据的功能的组件不是组件。组件中必须有一些功能会随时间变化,因此需要更新功能。如果您的组件不需要更新功能,那么您知道它不是组件,则应重新考虑设计。健康组件中的更新功能很有意义,例如,您希望您的NPC在一段时间内免受损坏。
Maik Semder 2011年

@Maik我的观点不是那些永远不会/永远不会改变的组件。我同意那是愚蠢的。他们只是不需要更新每个框架,而是在必要时被通知更改其信息。如果健康状况随着时间的流逝,那么将会有一个健康奖金组成部分的确有更新。我认为没有任何理由将两者结合起来。
ptpaterson 2011年

我还要指出,我发布的代码仅具有解释UpdateComponent概念所需的内容。它排除了组件之间所有其他形式的通信。
ptpaterson 2011年

Answers:


16

组件系统的主要优点之一是能够利用缓存模式-良好的icache和预测功能,因为您可以一遍又一遍地运行相同的代码;良好的dcache,因为您可以在同构池中分配对象,并且vtables任何,保持热。

构造组件的方式,这种优势完全消失了,实际上,与基于继承的系统相比,这种优势可能成为性能上的负担,因为您将使用vtables进行更多的虚拟调用和更多的对象。

您应该做的是为每种类型存储池,并分别迭代每种类型以执行更新。

这看起来像是正常的东西还是任何人使用的东西?我在这个问题上找不到任何东西。

它在大型游戏中并不常见,因为它没有优势。它在很多游戏中都很常见,但是从技术上讲它并不有趣,因此没有人对此进行撰写。

我该如何维护物理等组件的顺序,然后更改位置?这有必要吗?

像C ++这样的语言中的代码有一种自然的方式来执行顺序:按该顺序键入语句。

for (PhysicsComponent *c : physics_components)
    c->update(dt);
for (PositionComponent *c : position_components)
    c->update(dt);

实际上,这没有意义,因为没有以这种方式构造强大的物理系统-您无法更新单个物理对象。相反,代码看起来更像:

physics_step();
for (PositionComponent *c : position_components)
    c->update(dt);
// Updates the position data from the physics data.

谢谢。我在开始整个项目时就考虑了面向数据(与数据驱动不同)的问题,以防止发生缓存丢失等情况。但是,一旦我开始编码,我决定只做点有用的东西。原来,这使我更难受。严重的是,那篇失眠的文章很棒!
ptpaterson

@Joe +1是一个很好的答案。虽然我想知道什么是icache和dcache。谢谢
Ray Dey

指令缓存和数据缓存。有关计算机体系结构的任何介绍性文字均应涵盖它们。

@ptpaterson:请注意,Insomniac演示中存在一个小错误-组件类必须包含其名册索引,或者您需要将池索引单独映射到名册索引。

3

我认为,您所说的是合理且相当普遍的。可能会给您更多信息。


我认为我是在几周前见过该文章的。我已经能够将基于组件的实体系统的几个非常简单的版本编码在一起,当我尝试不同的方法时,每个版本都非常不同。试图将所有文章和示例整合到我想要并且能够做到的事情中。谢谢!
ptpaterson

0

我喜欢这些方法:

很快:避免在组件内保持更新行为。组件不是行为。行为(包括更新)可以在某些单实例子系统中实现。该方法可能还有助于批量处理多个组件的相似行为(可能在组件数据上使用parallel_for或SIMD指令)。

IUpdatable的想法和Update(gametime dt)方法似乎过于严格,并引入了其他依赖项。如果您不使用子系统方法,那可能很好,但是如果您使用子系统方法,则IUpdatable是冗余的层次结构级别。毕竟,MovingSystem应该知道它必须直接为具有这些组件的所有实体直接更新Location和/或Velocity组件,因此不需要某些中间的IUpdatable组件。

但是,尽管它们具有“位置”和/或“速度”组件,但您可以使用“可更新组件”作为跳过更新某些实体的机制。可更新组件可以具有可以设置为bool的标志,false并且将向每个可感知可更新子系统发出信号,通知该特定实体当前不应该进行更新(尽管在这种情况下,Freezable似乎是该组件更合适的名称)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.