1
\$\begingroup\$

I am developing a simple component/entity game engine in C# (https://github.com/alexvm/BlueShift) and have a few questions regarding the responsibility of certain items in the engine itself. I have the following:

  • Scene
  • Entity
  • Component

I am planning to have the scene maintain a collection of entities. To draw components on screen, the scene will loop over entities draw any components that extend from a drawable parent.

E.g. a drawable component might look like:

public class SpriteComponent : Drawable {

}

And in as scene I would call:

foreach(var entity in entities) {
    Scene.DrawComponents(entity.DrawableComponents());
}

Does the responsibility of each class make sense? I.e. does it seem like good design for the scene to maintain all entities and for the entity to present the drawable components to the scene to draw?

\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

This kind of defeats one of the main purposes of having components in the first place. What I think you actually want is something like this:

class SpriteSystem
{
    void SpriteSystem::Draw()
    {
        foreach(var sprite in sprites)
        {
           this.DrawSprite(sprite);
        }
    }
}

class Sprite
{
   public Sprite::Sprite()
   {
       SpriteSystem::Register(this);
   }

   public Sprite::Destroy()
   {
       SpriteSystem::Unregister(this);
   }
 }

Every time a sprite is created, it should register itself with the sprite system. It should unregister when it is destroyed. The sprite system draws all registered sprites. This has several significant advantages:

  • No need to find all the drawable components
  • Can take advantage of batching
  • Much better cache locality
  • No need for vtable lookups with virtual functions
  • No need to reference every possible type of thing in a generic "rendering system" as in the visitor pattern

Each and every type of component should have it's own system for drawing and updating itself. You should not have to reference entities or traverse a reference graph of entities to draw or update anything. The only purpose of entities is to loosely associate components into a container. That's one of the main points of having a component system.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the answer - would a system typically be a singleton class? It would seem strange to me for every type of render-able component to have its own sysytem.. would it not be better to have a render system for a base component such as a SpriteComponent? \$\endgroup\$
    – Alex
    Dec 9, 2014 at 19:26
  • \$\begingroup\$ You can use a singleton, but I prefer to pass around systems so I can configure them. Yes, you would want the system to operate on base classes, but as soon as the functionality differs a lot (like you're doing a lot of virtual function calls and checks) you will want to make a new system. Another option is you can have the Sprite class itself have static functions that replicate the sprite rendering system. Like you have "unregister" and "register" and "drawsprites" inside "Sprite" as static functions, which is nice and tidy. \$\endgroup\$
    – mklingen
    Dec 15, 2014 at 22:01
1
\$\begingroup\$

I'm currently using the Visitor Pattern ( Wikipedia link) as the basis of my rendering system for my game.

Basically, I have several interfaces defined to support this: (Note, this is basically an abstract recreation of part of my games system, and most likely does not reflect current code)

interface ISystem
{
    void Accept(EntityBase entity);
    void Accept(EntityActor entity);
    void Accept(EntityItem entity);
    // and so on
    void Draw(ComponentVisual visual, TransformComponent transform);
}

interface IAcceptee
{
    void Apply(ISystem system);
}

And then the classes

class RenderSystem : ISystem
{
    public void Accept(EntityBase entity)
    {
        // set effect here
        Draw(entity.GetComponent<VisualComponent>(), entity.GetComponent<TransformComponent>());
    }
    public void Accept(EntityActor entity)
    {
        // set effect here
        Draw(entity.GetComponent<VisualComponent>(), entity.GetComponent<TransformComponent>());
    }
    public void Accept(EntityItem entity)
    {
        // set effect here
        Draw(entity.GetComponent<VisualComponent>(), entity.GetComponent<TransformComponent>());
    }
    // etc
    public void Draw(ComponentVisual visual, TransformComponent transform)
    {
        // drawing code here
    }
}

// example of an entity
class EntityBase : IAcceptee
{
    public void Apply(ISystem system)
    {
        system.Accept(this);
    }

    // other entity data
}

And to use

ISystem render = new RenderSystem();

// ...

foreach (EntityBase entity in entities)
{
    if (entity.HasComponent<VisualComponent>())
        entity.Apply(render);
}

And yes, I do use Apply/Accept instead of Accept/Visit that the Wikipedia article uses in its examples. Personal preference.

The advantages of this is that the game has a collection of systems (may include different rendering systems), and I can swap between them (eg swapping between Dx9 and Dx10 3d rendering systems)

My components only contain data (position, visual, health, etc) and don't care about the entity they are attached to, my entities know they can apply a system which will use their components, and the systems only care about accepting entities.

You might want to move the entity.HasComponent<Component>() call into the system itself as part of an CanAccept function, which would be added to the ISystem interface, and implemented by each system. I would do the same, except I'm slightly hesitant about diving into a true ECS (Entity Component System) implementation, and I still have different entity classes with components explicitly declared (so instead of calling entity.HasComponent<VisualComponent>() I call entity.HasVisual instead.

I originally had Components for drawing, receiving input, updating, etc, but then I quickly ended up with duplicated components, components subclassing other components to extend it and add extra functionality, etc, and it was a mess. Moving to this pseudo Visitor Pattern based ECS has greatly simplified by own code, and made it much more understandable, not to mention the maintainability.

In the end, use what feels right for you. I was once stuck at the same place you are now, trying to decide how to implement a rendering system and what the best way was, and when I saw the Visitor Pattern, I knew that it is what I needed, and what I was going to use. You might feel the same when you spot some code or a design pattern that just seems to click and fit your requirements/needs.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the response, the visitor pattern is not something I am familiar with so learning about it has been very useful. \$\endgroup\$
    – Alex
    Dec 9, 2014 at 19:27
1
\$\begingroup\$

Usually you will have your game engine alongside a rendering engine.

The System which contains the Render Components will not do the drawing. It will ensure each Render Component is properly synchronised with the current animation, position etc...

Concerning the rendering it will happen outside of your system in the render engine as you will have special optimisations that are not practical with a list of objects, or you might even have multiple rendering engines depending on the platforms you are working with.

Same thing applies to physics where your physics components will contain the physics objects from your physics engine and simply ensure all position and movement data is properly synchronised with the game engine.

\$\endgroup\$

You must log in to answer this question.

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