0
\$\begingroup\$

There are countless questions here about whether entities should draw themselves or not. The answer is almost universally no, there should be a renderer. And I do see a point in having a single place where everything is rendered. Makes asset management easier and the code less convoluted.

However, I'm struggling to find a good approach to implement this in my 2D game. Currently, every entity is told to draw itself, and I pass the renderer object to it. Now, I want the renderer to draw each entity, but I don't want the renderer to actually know about entities, so I need a more generic class, e.g. RenderObject, and a way to construct render objects from entities.

One obvious approach would be to have a GetRenderObject() method on each entity. However, some of my entities actually draw multiple textures, relative to their position, so I'd probably need a GetRenderObjects() function instead.

But that wouldn't really remove any rendering logic from my entities. I'd still have to refer particular assets and their positions relative to the entity position in the render objects I create.

So my question is: Doesn't having a renderer mean that my entities don't have any drawing logic anymore? If so, who will generate these render objects? Does it even make sense to take the render objects detour?

And, bonus question: How can this work together with a UI system? I'd rather not have the renderer know about widgets and containers and such, so my hunch so far is to have widgets stick to drawing themselves.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Remember that the point of "entities should not draw themselves" is to remove the dependency on the renderer from the entity system and to allow render logic to be factored out into a few places as possible. Both increase quality the code through improved maintainability and decreased coupling.

A RenderObject class is a good approach, however, if the entity itself generates it, you're still in the domain where the entity "knows things" about how to render objects. There is no difference between an entity that takes a renderer and says "renderer, put a textured quad here" and one that has a function that returns an object saying "renderer, draw a textured quad there."

Instead, remember that non-member functions increase encapsulation and move that logic for assembling a render object for a given entity out to a level of abstraction that already knows about both the entity system and the renderer: the game itself. This way there is no loss of generality. The "game" object is already the thing that ties all your various disparate systems together.

At a high level, in other words, your game loop might contain something like this:

var renderables = new List<RenderObject>();
foreach (entity in the world) {
  // Perhaps cache instead of reconstruct here, if your specific
  // render object is "expensive" to build:
  var renderable = CreateRenderObjectForEntity(entity);
  renderables.Add(renderable);
}

renderer.RenderAll(renderables);

CreateRenderObjectForEntity is a method that exists on the game object itself, or some system at a similar layer of abstraction, and it's sole job is to take an entity, extract useful bits of information about it, and turn that into a RenderObject (or multiple render objects, if that's needed).

An entity's position data is certainly useful for things beyond rendering, so it can remain in the entity. Things like texture references (as in, just strings or IDs) aren't actually render-API objects and so they could remain in the entity, particularly if you are using a component-based approach to entity construction that is so popular these days: an "aspect" or "visual" component can be added that holds all the render-only data like texture or shader references. If you don't have a component-based system, you can still offload some of that information to external databases. For example, an entity could have a "race" and "gender" like in most RPGs, and the you can actually look up the texture ID reference in some catalog of race/gender combinations you load from one of your data files.

This approach scales naturally to user interface: user interface is another system that you fit an adapter in front of to create render objects for all the appropriate screen / widget elements in the current scene.

\$\endgroup\$
3
  • \$\begingroup\$ Free functions sounds like a good approach - that way I can easily have class-based rendering logic and also put the logic wherever I think it belongs without adding it to the hierarchy. \$\endgroup\$
    – futlib
    Commented Mar 18, 2014 at 7:56
  • \$\begingroup\$ But one thing I'm wondering about: Wouldn't I get to the proper logical separation by simply turning all my entity specific render functions into free functions? Or do you think I can't call my renderer thingie renderer if I don't put something like a RenderObject in the middle? \$\endgroup\$
    – futlib
    Commented Mar 18, 2014 at 7:58
  • \$\begingroup\$ You would achieve separation by making them free functions, but you still don't achieve a unified code path. It's a step in the right direction though. \$\endgroup\$
    – user1430
    Commented Mar 18, 2014 at 15:34

You must log in to answer this question.

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