我正在用ECS框架开发自己的游戏引擎。这是我使用的ECS框架:
- Entity:只是一个连接其组件的ID。
- 组件:一个存储纯数据的结构体,根本没有方法(所以我可以写。xsd来描述组件并自动生成C++结构代码)。
- 系统:处理游戏逻辑。
- EventDispacher:将事件分派给订阅方(系统)
但我对系统应该如何更新组件的成员并通知其他系统感到困惑?例如,我有一个这样的TransformComponent:
struct TransformComponent
{
Vec3 m_Position;
Float m_fScale;
Quaternion m_Quaternion;
};
显然,如果可渲染实体的TransformComponent的任何成员被更改,则RenderSystem还应在渲染下一帧之前更新着色器统一“worldMatrix”。所以如果我做“comp-〉m_Position = ...”,那么RenderSystem应该如何“注意到”TransformComponent的变化呢?我提出了三个解决方案:
1.更新成员后发送UpdateEvent,并在相关System中处理该事件。这是丑陋的,因为一旦系统修改组件数据,它必须发送这样的事件:
{
...;
TransformComponent* comp = componentManager.GetComponent<TransformComponent>(entityId);
comp->m_Position = ...;
comp->m_Quaternion = ...;
eventDispatcher.Send<TransformUpdateEvent>(...);
...;
}
1.将成员设为私有,并为每个组件类编写一个具有set/get方法的相关系统(将事件发送 Package 在set方法中)。这会带来很多繁琐的代码。
1.不做任何更改,但添加“可移动”组件。RenderSystem将在Update()方法中使用“Movable”组件迭代更新可渲染实体。这可能不能解决其他类似的问题,我不确定性能如何。
我想不出一个优雅的方法来解决这个问题。我应该改变我的设计吗?
2条答案
按热度按时间rta7y2nd1#
我认为在这种情况下,最简单的方法将是最好的:你可以在读/写
Transform
组件的组件中保存一个指向它的指针。我不认为使用事件(或其他一些间接方式,如观察者)解决了这里的任何真实的问题。
Transform
组件非常简单--它不会在开发过程中被更改。对它的抽象访问实际上会使代码更加复杂,更难维护。Transform
是一个组件,它会频繁地为许多对象更改,甚至你的大多数对象会在每帧更新它。每次有变化时发送事件都有成本-可能比简单地将矩阵/向量/四元数从一个位置复制到另一个位置要高得多。1.我认为使用事件或其他一些抽象不能解决其他问题,比如多个组件更新同一个
Transform
组件,或者组件使用过时的转换数据。1.通常,渲染器仅在每帧复制渲染对象的所有矩阵。在渲染系统中缓存它们是没有意义的。
像
Transform
这样的组件经常使用。使它们过于复杂可能是引擎的许多不同部分的问题,而使用最简单的解决方案,指针,将给予您更大的自由。顺便说一句,还有一种非常简单的方法可以确保
RenderComponent
在更新后读取transform *(例如:例如通过PhysicsComponent
)-您可以将工作分为两个步骤:Update()
,其中系统可以修改组件,以及PostUpdate()
,其中系统只能从组件读取数据例如,
PhysicsSystem::Update()
可能会将转换数据复制到相应的TransformComponent
组件,然后RenderSystem::PostUpdate()
可以只从TransformComponent
读取,而不会有使用过时数据的风险。xxb16uws2#
我认为这里有很多事情要考虑。我将分成几部分,首先讨论你的问题。
1.关于您的解决方案1.考虑一下,你可以用布尔值做同样的事情,或者分配一个空的组件作为标记。很多时候,在ECS中使用事件会使系统架构过于复杂。至少,我倾向于避免它,特别是在较小的项目中。请记住,作为标记的组件基本上可以被认为是一个事件。
1.您的解决方案2是我们在1中讨论的后续。但它揭示了这种一般方法的一个问题。如果你在几个系统中更新你的TransformComponent,你不能知道TransformComponent是否真的改变了,直到最后一个系统更新了它,因为一个系统可以把它移到一个方向,另一个系统可以把它移回来,让它在你的滴答开始。你可以通过在一个系统中更新一次TransformComponent来解决这个问题。..
1.看起来像你的解决方案3。但也许反过来。您可以在多个系统中更新MovableComponent,然后在ECS管道中,使用单个系统阅读MovableComponent并写入TransformComponent。在这种情况下,只允许一个System在TransformComponents上写入是很重要的。在那个时候,有一个布尔值指示它是否已经被移动,将完美地完成这项工作。
在此之前,我们已经用性能(因为当TransformComponent没有改变时,我们避免了在RenderSystem上的一些处理)来换取内存(因为我们以某种方式复制了TransformComponent的内容)。
最后一个是我的首选解决方案。但这也取决于您的系统的特性和您的关注点。和往常一样,不要盲目地优化性能。先测量,再比较。