2
\$\begingroup\$

To avoid confusion about the ECS terms I'll use, I explain them here quickly. I use entt terminology and set-up my architecture like this:

  • Entity: is only an id (entt::entity)
  • Component: is a struct holding plain data (int, floats, etc.)
  • System: is a function looping over a family of components (using entt::registry.view)

In my game, I model a space ship that can have multiple engines. The engines apply a force on the ship each frame so the ship accelerates. I implemented it like the following:

Ship is an entity, Engine is an entity. Ship has the components AppliedForces, Mass and Velocity. Engine has the components Parent and Thrust. Parent is a component containing the parent's id of the engine (in this case a ship entity).

I have two systems: ThrustSystem goes through all entities that have the components Thrust and Parent. It looks up the AppliedForces component of the parent and adds the thrust vector to it. Then the AccelerationSystem runs through all entities with the components AppliedForces, Mass and Velocity. It adds the applied forces to entity's velocity. In code, it looks like this:


void ThrustSystem::update(entt::registry& entities)
{
  auto view = entities.view<Thrust, Parent>();
  for (auto [thrust, parent]: view)
  {
    auto& applied_forces = entities.get<AppliedForces>(parent.id);
    applied_forces.vector += thrust.vector;
  }
}

void AccelerationSystem::update(entt::registry& entities, dt)
{
  auto view = entities.view<AppliedForces, Mass, Velocity>();
  for (auto [applied_forces, mass, velocity]: view)
  {
    velocity.vector += applied_forces.vector * mass * dt;
  }
}

This works perfectly for me, the systems are decoupled and work independently from each other and I don't experience any performance issues (I model max. 1000 ships with an average number of 4 engines each). So I will stick to this implementation in my current game.

But I am actually curious and I would like to learn more about the "correct" usage of ECS in game code: is this a recommended approach to gain high performances and avoid cache misses? This is my first project with ECS and I am not an expert in CPU stuff at all but naively-thinking I would say there are two "bottlenecks" in my code:

  • Look-up time for the AppliedForces component of the parent entity in ThrustSystem
  • The AppliedForces is probably further away from Thrust and Parent in memory. So I introduced a cache miss.

At first, are my assumptions correct? And if yes, how would one avoid these bottlenecks?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Well it looks like its 2n, I think with entt, there are some methods to specifically place things as you wish in memory. If you really wanted you could try to do alignas stuff or even bitfields (that seems too far). But really I dont know, sorry no one has answered your question, its a good one :( Really I am pretty new to entt though specifically \$\endgroup\$ – The Floating Brain Jul 16 '20 at 16:01
  • 1
    \$\begingroup\$ Also see entt::basic_registry< Entity >::insert Dunno really though, I know entt is optimized, just dont know how well a case like this is taken into account \$\endgroup\$ – The Floating Brain Jul 16 '20 at 16:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.