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.