9
\$\begingroup\$

Apologies if this question has been answered before, but after relentless searching I couldn't find anything.

As many, I've recently jumped on the ECS-bandwagon, and I am currently killing some time by making a modest ECS-game. The game is a somewhat simple 2D-platform game. It is programmed in plain JS.

The layout of the game is essentially as following:

The core of the game is the Engine, the Engine runs the game-loop and holds an EventManager, responsible for raising events, and an EntityManager, responsible for containing all components of all entities.

All logic is done by systems. The systems registers event-handlers (i.e. member-functions) with the EventHandler for specific EventTypes, which gets called when an event of that type is raised.

For example:

this.eventManager.registerHandler(EventType.EVENT_RENDER, this.render, null, this);

I recently switched from calling all systems relevant functions explicitly in the Engine to this pattern.


But over to my problem and question.

For rendering I have a RenderSystem. This system contains references to several type of drawable/animatable-components which it, you guessed it, renders.

Until recently, this system also contained a reference to another system, MapSystem, which spatially indexes all entities. The reason for this reference was to be able to call a function along the lines of

mapSystem.search(frame_bound);

effectively pruning away all entities not needing to be rendered.

So I have a couple of questions regarding this:

  1. Is it very bad practice for systems to communicate directly? Something about it just doesn't smell right to me.

    I see how it might severely complicate your code if you have say 100 systems, and each system holds references to many of the other systems. Hello O(n^2).

  2. If I am not to hold inter-system references, how do I perform communication like described above?


My initial thoughts was to create an entity whose primary purpose were to hold the necessary information (i.e. all entities currently in the frame). Then let MapSystem write to and RenderSystem read from this entity.

But this also seems to be rather unclean to me. Especially as MapSystems search function might be useful to call in many different contexts. Creating an entity per calling-context doesn't really seem like a good way to go about it either.


TL;DR: Is it bad for systems in a ECS-game to communicate directly and hold references to each other?

\$\endgroup\$
2

1 Answer 1

4
\$\begingroup\$

Basically speaking, your systems should not need to talk to each other. There may be cases where systems implicitly interact, but this interaction should not be explicit. If you need to have systems interact with each other, then you probably did not define the systems properly.

Before I dig into some examples to illustrate the point I want to define what kind of entity-component system I am talking about. You have entities that do nothing and components that define their behavior. Actual behavior is implemented in Systems. because that is the efficient thing to do. Entities have properties and events, which may be read, written or triggered by multiple components.


Let's assume that you are implementing a missile. The missile's basic behavior is controlled by a guidance component (a script), the actual motion is controlled by a rigid body physics component, detonation is controlled by a trigger component (a script), the visual representation is done through a visual mesh component and the sound is emitted through a point sound component. The guidance component sets the force vector property and the rigid body physics moves the entity and thus also sets position and orientation (among others). The trigger component listens to the collision event, so when a collision with an other entity happens it springs into action and ensures that mayhem happens.

To implement the missile, the graphic, sound, physics and scripting system are involved. Actual interaction is only happening though their components.


Let's assume you have a photo realistic game and the graphic system is the primary means to render the scene. You want a map as overlay. There are two ways to implement this, either you extend the graphic system to also draw maps (bad) or you add a mapping system (good). To access the actual graphic device the mapping system does not call the graphic system. You extract the graphic device (hardware abstraction) from the graphic system and then pass the mapping system and the graphic system the graphic device and they use it in turn.


Finally I find the notion that the graphic system asks the mapping system for a list of visible items odd. The graphic system should maintain a list of visible items, preferably stored in some specially optimized structure like a quad tree. You may get away with it in the context of a 2D game, but the moment you go 3D you get into real trouble.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ A little coupling between systems is not a bad thing in my experience, and is clearer than relying on events or similar to communicate between systems. I tend to have a few coupled systems (referenced systems on the rightmost columns) - they usually act as "assisting" systems, such as the GridSystem. \$\endgroup\$
    – junkdog
    Commented May 7, 2015 at 17:43
  • \$\begingroup\$ Thanks for the response rioki! So essentially, what you are saying is that in all systems where I need to extract entities/components from specific regions I should store copies of relevant entities (in a quadtree)? My reasoning for having the mentioned coupling between the two systems (and also movementsystem and mapsystem) was so that I did not have to have a quadtree per relevant system, but instead have one located in the mapsystem. \$\endgroup\$
    – trmd
    Commented May 8, 2015 at 14:08
  • \$\begingroup\$ @trmd You have multiple options. You can have one quad tree in the scene or you can have one per system. The thing is that each system has only the relevant entities in it's structure and may use different organisational structures. \$\endgroup\$
    – rioki
    Commented May 10, 2015 at 8:40
  • 1
    \$\begingroup\$ @junkdog You must always approach any problem with some pragmatism. Ideally, loose coupling is the optimum. When you can remove or replace a system without changing anything else, you did it right. But sometimes the reduction in coupling increases the complexity of the system drastically, then you must find balance point where the gain in flexibility is not lost by the increased code base. \$\endgroup\$
    – rioki
    Commented May 10, 2015 at 8:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .