Search code examples
c++entitysoftware-designentity-systementity-component-system

Entity Component System - Components requiring each other


I have written an entity component system for my game (C++). I have then refactored my render system to work with Entities / RenderComponents rather than some virtual drawable interface. Their are some classes for which I don't think it makes too much sense to force them to be a component. One of those classes is the map.

My map class consists of a tiled terrain class and some other data (not important). The tiled terrain class manages multiple layers in form of (what is at the moment) the TiledTerrainLayer class. Before refactoring the render system I simply inherited from Drawable and Transformable to enable this class to be drawn by the render system. Now it is required to be an entity with at least a TransformComponent and some RenderComponent.

Now, the TiledTerrainLayerRenderComponent should really only own the vertices and a reference of the texture and maybe a flag for whether it has been created yet. The TiledTerrainComponent would then own the list of tile indecies as well as tile and map size.

Now my problem is that when I set a tile (using something like a SetTile(size_t tileIndex, const Position & pos) method, I also have to update texture coordinates of the vertex array.

I am generally fine with one component requiring another component. For example the SpriteRenderComponent requires a TransformComponent and I am also fine with one component accessing the information of another. E.g. the GetBoundingBox() method uses the position of the transform component.

What I want to avoid is two components 'cross-referencing' each other like it would be the case with the TiledTerrainComponent (TTC) and TiledTerrainRenderComponent. (TTRC) (The TTRC gets the TTC's tileIndexList to create itself and the TTC calls the TTRC's UpdateVertices() method when its SetTile() method is called.

Lastly, I am aware that components should mainly be data. I have only added methods that directly get or modify that data such as SetTile() or GetTexture(). Would a system be viable in the case described above and if yes how would it look like?


Solution

  • It sounds like all you need here is a Dirty Flag.

    When you change tile index, size, or other properties on your Tiled Terrain, you do not immediately phone the Tiled Renderer to update its vertices (after all, you might have many tile updates yet to come this frame — it could be wasteful to recalculate your vertices every time)

    Instead, the Tiled Terrain renderer just sets its internal hasBeenModifiedSinceLastUse flag to true. It doesn't need to know about the Renderer at all.

    Next, when updating your Tiled Renderer just prior to drawing, you have it ask its Tiled Terrain whether it's been updated since the last draw (you could even query a list of updates if you want to target the changes). If so, you update the vertices in one big batch, for better code & data locality.

    In the process, you reset the modified flag so that if there are no updates on subsequent frames you can re-use the last generated set of vertices as-is.

    Now your dependency points only one way — the renderer depends on the tile data, but the tile data has no knowledge of the rendering apart from maintaining its flag.