I'm currently creating a game engine for my personal project.
And I'm currently implementing Entity and Component System.
Classes and Interfaces Definitions
Here the classes and interfaces that involved to this problem:Entity: abstract class for any entity in the game, it does not implement any interface or class. It hasChildrenmechanism, so anEntitycan add anotherEntityas it child and able to remove it any time, it can also contains multipleComponent.Component: abstract class for any component in the game. TheComponentis used to add, remove and / or modify the behavior or ability of anEntity. I use this to aid C# limitation that a class cannot inherit multiple classes at a time.Entitycan contains multipleComponent, however, it cannot contains multipleComponentwith same type at the same time.Scene: a class that represent a "Screen" or "State" in the game.Scenecan contains multipleEntityobjects and responsible for render theEntitythat implementIRenderableas well as updatingEntitythat implementIUpdatableandIInputableIRenderable: Represent an object that can be rendered and displayed on the screen.IUpdatable: Represent an object that can be updated each frame in the game.IInputable: Represent an object that able to receive player input.
The Codes
So here the simplified codes:Entity:
public abstract class Entity
{
private List<Component> _components = new List<Component>();
private List<Entity> _children = new List<Entity>();
public void AddChild(Entity child)
{
_children.Add(child);
}
public void RemoveChild(Entity child)
{
_children.Remove(child);
}
public Entity[] GetChildren()
{
return _children.ToArray();
}
public void AddComponent<T>()
where T : Component
{
if (_components.Find((c) => c is T) != null)
return; // each entity can only contains one type of component at the same time
var component = (T)Activator.CreateInstance(typeof(T));
component.Owner = this;
_components.Add(component);
}
public void RemoveComponent<T>()
where T : Component
{
var component = _components.Find((c) => c is T);
if (component == null)
return;
component.Owner = null;
_components.Remove(component);
}
public Component[] GetComponents()
{
return _components.ToArray();
}
}
Component:
public abstract class Component
{
public Entity Owner { get; protected internal set; }
}
Scene:
public class Scene : IRenderable, IUpdatable, IInputable
{
private List<Entity> _entities = new List<Entity>();
public bool Enabled { get; set; }
public bool Visible { get; set; }
public void Add(Entity entity)
{
_entities.Add(entity);
}
public void Remove(Entity entity)
{
_entities.Remove(entity);
}
// Render, Update and Input function in the Scene will automatically called at the game loop by game window
public void Render(RenderTarget target, RenderStates states)
{
if (Visible)
{
foreach (var entity in _entities)
{
if (entity is IRenderable)
((IRenderable)entity).Render(target, states);
}
}
}
public void Update(double delta)
{
if (Enabled)
{
foreach (var entity in _entities)
{
if (entity is IUpdatable)
((IUpdatable)entity).Update(delta);
}
}
}
public void Input(InputEventArgs e)
{
if (Enabled)
{
foreach (var entity in _entities)
{
if (entity is IInputable)
((IInputable)entity).Input(e);
}
}
}
}
The Interfaces:
public interface IRenderable
{
bool Visible { get; set; }
void Render(RenderTarget target, RenderStates states);
}
public interface IUpdatable
{
bool Enabled { get; set; }
void Update(double delta);
}
public interface IInputable
{
bool Enabled { get; set; }
void Input(InputEventArgs e);
}
Questions
1. Does any class / interface violates or lacking it's capability and / or functionality from it's name? for example, `IRenderable` enforce the class that implement it to implement `Visible` property, which mean all "Renderable" object should have "Visible" state, is such design correct?At the shown code above, the
Sceneis not responsible to check theEntitychildren and it's components, and it also possible to add anotherEntityas a child while implementing interface that not implemented by it's parent. Consider following example:public class CustomEntity : Entity { // Some codes here... } public class Sprite : Entity, IRenderable { public bool Visible { get; set; } // ... public void Render(RenderTarget target, RenderStates states) { // If the sprite is not visible, it should not render itself and ignore the children if (!Visible) return; // Render the sprite here... // Since scene does not check the Entity children, // We need to render the child and components in case they implement IRenderable foreach (var child in GetChildren()) { if (child is IRenderable) { if (((IRenderable)child).Visible) ((IRenderable)child).Render(target, states); } } foreach (var component in GetComponents()) { if (component is IRenderable) { if (((IRenderable)component).Visible) ((IRenderable)component).Render(target, states); } } } }
the CustomEntity does not implement IRenderable interface, however, it is possible to do something like this:
var myEntity = new CustomEntity();
var mySprite = new Sprite();
myEntity.AddChild(mySprite);
and of course mySprite won't be rendered when myEntity is added to the Scene, the same thing applies to the Component. Is this correct behavior? or should Scene handle those stuffs?
That's all for now, in case you find something weird with the design, I'm open for suggestion. I'll update the question if I've got something more to ask.