I have a working ECS-system set up, largely inspired by this brilliant article (not at all required reading to be able to answer my question) on https://indiegamedev.net/ showcasing an ECS-system with proper data locality. I've modified it quite a bit, but the general layout of the API is pretty much the same.
struct SystemFunctor
{
void operator()
(float timeSteps, const std::vector<EntityID>&,/*(arrays ->)*/PhysicsComp*, RenderComp*);
};
void system(float timeSteps, const std::vector<EntityID>&,/*(arrays ->)*/PhysicsComp*, RenderComp*);
...
SystemFunctor f;
engine.registerSystem<Engine::RENDER_LAYER_1>(f);
engine.registerSystem<Engine::RENDER_LAYER_1>(system);
/* Both of these calls register a system that operates on entities with at least a
PhysicsComp and a RenderComp, and will be called when the system layer RENDER_LAYER_1 is run. */
As the title reads, my question is: How can entities that have no component types in common interact with each other? Since a system only receives data from entities whose types are a subset of the entity composition it operates on, how could it receive the data it needs to perform interactions between wildly different entities?
To get a little more specific, here is the current problem I'm trying to solve:
I'm making a small exercise program where the user draw points on the screen with the mouse, and a point-quadtree for all the points is recomputed and drawn (as boxes) on the screen when new points are added.
The adding and rendering of the points was easy - just one system that checks for input and creates the points and another one for rendering them. But when it comes to storing and managing a quad-tree that can come into contact with the point entities I'm at a loss.
Naturally, you would have some kind of quad-tree component:
struct QuadTreeComp
{
// Whatever data your quad-tree implementation uses
};
But from there, I can't seem to figure it out. How do you set up a system that operates on one entity (the one with the QuadTreeComp
) while also operating on a bunch of other entities, i.e. the points?
One possible solution I could see is that the system functor that updates the quad-tree doesn't actually operate on QuadTreeComp
, but on the points. In its constructor it instantiates an entity with the QuadTreeComp
and stores the entity id. Then in its actual function body, it can get the quad-tree component using the id and update it with the points.
class UpdateQuadTree
{
public:
UpdateQuadTree(Engine& engine):
engine_{engine},
quadTreeEntity_{ engine.newEntity() }
{
engine.add<QuadTree>(quadTreeEntity_);
}
void operator()
(float timeSteps, const std::vector<EntityID>& entities, Vector2f* points)
{
QuadTree* quadTree = engine.getComponent<QuadTree>(quadTreeEntity_);
/*
^
|
Already an existing function in my ECS-API
*/
// Update the quad-tree using the points...
}
private:
EntityID quadTreeEntity_;
Engine& engine_;
};
The problem with this one is that don't see how other systems that operate on the quad-tree, like the one that renders it, would know the id of the entity.
What would be the textbook-ECS-way of performing these interactions?