如何在ECS框架中更新组件数据和通知系统
How to update component data and inform systems in ECS framework?
我正在用ECS框架开发自己的游戏引擎。这是我使用的ECS框架:
- 实体:只是一个连接其组件的ID
- 组件:一个存储纯数据的结构,根本没有方法(所以我可以写.xsd来描述组件并自动生成C++结构代码)
- 系统:处理游戏逻辑
- EventDispacher:将事件分派给订阅者(系统)
但我很困惑系统应该如何更新组件的成员并通知其他系统?例如,我有一个像这样的TransformComponent:
struct TransformComponent
{
Vec3 m_Position;
Float m_fScale;
Quaternion m_Quaternion;
};
显然,如果可渲染实体的TransformComponent的任何成员发生更改,RenderSystem也应在渲染下一帧之前更新着色器统一的"worldMatrix"。那么,如果我在系统中执行"comp->m_Position=…",RenderSystem应该如何"注意到"TransformComponent的更改?我提出了3个解决方案:
更新成员后发送UpdateEvent,并在相关系统中处理该事件。这很难看,因为一旦系统修改组件数据,它就必须发送这样的事件:
{ ...; TransformComponent* comp = componentManager.GetComponent<TransformComponent>(entityId); comp->m_Position = ...; comp->m_Quaternion = ...; eventDispatcher.Send<TransformUpdateEvent>(...); ...; }
使成员私有,并为每个组件类编写一个具有set/get方法的相关系统(在set方法中包装事件发送)。这将带来许多繁琐的代码。
不要更改任何内容,但添加"可移动"组件。RenderSystem将使用update()方法中的"Movable"组件迭代更新可渲染实体。这可能无法解决其他类似的问题,我也不确定性能。
我想不出一个优雅的方法来解决这个问题。我应该改变我的设计吗?
我认为在这种情况下,最简单的方法将是最好的:您可以在读取/写入Transform
组件的组件中保留一个指向该组件的指针
我不认为使用事件(或其他间接方式,如观察者)可以解决这里的任何实际问题。
-
Transform
组件非常简单,在开发过程中不会改变。对它的抽象访问实际上会使代码变得更加复杂和难以维护。 -
Transform
是一个会为许多对象频繁更改的组件,甚至可能您的大多数对象都会在每帧更新它。每次发生更改都要发送事件,这可能比简单地将矩阵/向量/四元数从一个位置复制到另一个位置要高得多。 -
我认为使用事件或其他抽象不会解决其他问题,比如多个组件更新同一个
Transform
组件,或者组件使用过时的转换数据。 -
通常,渲染器只在每帧复制渲染对象的所有矩阵。在渲染系统中缓存它们是没有意义的。
经常使用Transform
等组件。在发动机的许多不同部分,使它们过于复杂可能是一个问题,而使用最简单的解决方案,指针,会给你更大的自由度。
BTW,还有一种非常简单的方法可以确保RenderComponent
在更新后(例如通过PhysicsComponent
)将读取转换-您可以将工作分为两个步骤:
-
Update()
,其中系统可以修改组件,以及 -
PostUpdate()
,其中系统只能从组件读取数据
例如,PhysicsSystem::Update()
可以将转换数据复制到相应的TransformComponent
组件,然后RenderSystem::PostUpdate()
可以直接从TransformComponent
读取,而不会有使用过时数据的风险。
我认为这里有很多事情需要考虑。我将分为几部分,先讨论你们的要求。
-
关于您的解决方案1。考虑一下,您可以对布尔值执行同样的操作,或者指定一个充当标记的空组件。很多时候,在ECS中使用事件会使您的系统架构过于复杂。至少,我倾向于避免它,特别是在较小的项目中。请记住,充当标记的组件基本上可以被认为是一个事件。
-
您的解决方案2遵循了我们在1中讨论的内容。但它揭示了这种通用方法的一个问题。如果您在多个系统中更新TransformComponent,则在上一个系统更新之前,您无法知道TransformComponent是否真的发生了更改,因为一个系统可能会将其向一个方向移动,而另一个系统则可能会将它向后移动,使其与勾选开始时一样。你可以通过在一个系统中只更新一次TransformComponent来解决这个问题。。。
-
这看起来像你的解决方案3。但也许相反。您可以在多个系统中更新MovableComponent,稍后在您的ECS管道中,拥有一个系统,读取您的MovableComponent并写入您的TransformComponent。在这种情况下,重要的是只有一个系统可以在TransformComponents上进行写入。在那个时候,有一个布尔值来指示它是否被移动,这将完美地完成任务。
在此之前,我们一直在用性能(因为在TransformComponent没有更改的情况下,我们避免了在RenderSystem上进行一些处理)换取内存(因为我们正在以某种方式复制TransformComponent的内容。
- 另一种不必添加事件、布尔值或组件的方法是在RenderSystem中执行所有操作。基本上,在每个RenderComponent中,您可以保留上次更新TransformComponent的副本(或哈希),然后对其进行比较。如果不相同,请渲染它,然后更新副本
// In your RenderSystem... if (renderComponent.lastTransformUpdate == transformComponent) { continue; } renderComponent.lastTransformUpdate = transformComponent; render(renderComponent);
最后一个是我的首选解决方案。但这也取决于你的系统的特点和你关心的问题。和往常一样,不要盲目地选择性能。先测量,然后比较。
- 防止主数据类型C++的隐式转换
- 用于访问容器<T>数据成员的正确 API
- 嵌套在类中时无法设置成员数据
- 使用流处理接收到的数据
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 如何将 txt 文件中的行分隔为组件C++
- 在cuda线程之间共享大量常量数据
- C++将文本文件中的数据读取到结构数组中
- 如何在C++中序列化结构数据
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 如何在ECS框架中更新组件数据和通知系统
- 使用蓝牙组件将数据从Android手机传输到串行设备时出现问题
- TSQLQUERY和数据感知组件的更新
- 有关C++/组件数据布局、数据成员访问、方法的一般问题
- 在随机分离的数据上标记连接的组件
- 面向组件系统中的灵活数据消息传递
- 从文本文件中读取组件矢量数据到结构数组
- 为组件构建数据池
- openCV警告组件数据类型不匹配