1
\$\begingroup\$

I am in the process of writing an entity component model game engine, and I wanted to ask about the best way to lay out the code. I have run into an issue that I know will quickly cause me to run into more problems, down the road. In short, I currently have a render component, that I attach to the entity class. The problem is, I need the render component to signal the render engine, and add itself to it.

I thought about creating a singleton for the render system, but I quickly realized that was a bad road, and sooner or later, I would be creating singletons for just about everything (Ex, physics for the physics component). What I am currently thinking of doing is creating the base engine class as my only singleton. The engine class is responsible for initializing all of the systems available, such as the graphics and file systems, storing references to them, calling the game loops update method, and keeping track of all the entities in the game. If I did that, then my components or any other part of the code could easily reference the important systems they need to deal with via a simple Engine.MySystem. In the case of my render component, Engine.Graphics3D.addModel().

I am wondering is if this is an acceptable way of doing things, or not. If not, what would be better? I could, in theory, pass a reference to the GFX system to the render component, but that seems like a really bad idea. I feel like this is a nice middle ground between the two options.

\$\endgroup\$

2 Answers 2

5
\$\begingroup\$

No, I don't think you should do it this way. All you're doing here is hiding the dependencies and design flaws that appear to necessitate the singleton in the first place. In some ways, your suggested engine singleton is worse.

In any context, in any code, you can always replace a singleton with explicitly dependency injection (that is, passing a instance of the would-be singleton as a parameter) and plumbing that instance through the layers of your API. When you do that, the more layers you have to plumb the instance through, the more flawed your current design is for handling the dependency relationship in question.

Here's the crux of your design flaw:

In short, I currently have a render component that I attach to the entity class. The problem is, I need the render component to signal the render engine and add its self to it.

(Emphasis mine.)

This implies that you can directly create a render component without a renderer itself. However, that fact doesn't actually gel with the architecture of the rest of your code, since you are having to go and look up some ubiquitous renderer instance and register the component with it. Invert that relationship, and have the renderer be the interface through which you create render components. That is, instead of what you have now, which looks like

RenderComponent c; // secretly talks to renderer, hiding a dependency

implement something like

RenderComponent c = someRenderer.CreateComponent();

This makes the relationship between those two interfaces more explicit and clear to the consumer of the code, and avoids your need for a singleton. Obviously the factory interface that products entities will need a reference to this "someRenderer," (in case creating a new entity would require creating a rendering component) but you can just pass that explicitly to the entity factory as well:

struct EntityFactory {
  EntityFactory (Renderer * renderer, /* other parameters */);
};

Here, you are only passing the renderer itself once. That's hardly egregious and it's much clearer and self-documenting. You may also need to pass other subsystem references to the entity factory, such as AI or physics systems, but that's okay too, because the entity factory needs those to do it's job.

Explicit is far superior to implicit.

\$\endgroup\$
4
\$\begingroup\$

Your entities do not need to know about a renderer, all they have to care about is sending a notification, "Hey! Someone please render me!" and be done with it.

Please consider the following design (NOTE: heavily simplified):

When the GraphicsSystem is initialized it basically says to the EventManager to notify it whenever something wants a model rendered.

The Model in turn, tells the EventManager that it mesh should be rendered every frame. The GraphicsSystem is notified and adds the mesh in its internal render queue.

The GraphicsSystem does not render anything in its render queues until the Render method is called.

The advantages with this design are:

  1. Scalable
  2. Loose coupling between objects
  3. Rendering can be placed in a separate thread without much hassle
  4. Rendering can be optimized since you now have a batch of entities to render
  5. Easily replace a rendering engine with a different one
  6. No singletons
  7. You can have multiple render queues.
  8. You can remove the renderer completely and the code wont break, the components will simply not be rendered.
\$\endgroup\$

You must log in to answer this question.

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