Okey, what I know so far; The entity contains a component(data-storage) which holds information like; - Texture/sprite - Shader - etc
And then I have a renderer system which draws all this. But what I don't understand is how the renderer should be designed. Should I have one component for each "visual type". One component without shader, one with shader, etc?
Just need some input on whats the "correct way" to do this. Tips and pitfalls to watch out for.
Answer
This is a difficult question to answer because everyone has their own idea about how an entity component system should be structured. The best I can do is share with you some of the things I have found to be most useful for me.
Entity
I take the fat-class approach to ECS, probably because I find extreme methods of programming to be highly inefficient (in terms of human productivity). To that end, an entity to me is an abstract class to be inherited by more specialized classes. The entity has a number of virtual properties and a simple flag that tells me whether or not this entity should exist. So relative to your question about a render system, this is what the Entity
looks like:
public abstract class Entity {
public bool IsAlive = true;
public virtual SpatialComponent Spatial { get; set; }
public virtual ImageComponent Image { get; set; }
public virtual AnimationComponent Animation { get; set; }
public virtual InputComponent Input { get; set; }
}
Components
Components are "stupid" in that they don't do or know anything. They have no references to other components, and they typically have no functions (I work in C#, so I use properties to handle getters/setters - if they do have functions, they are based around retrieving data that they hold).
Systems
Systems are less "stupid", but are still dumb automatons. They have no context of the overall system, have no references to other systems and hold no data except for a few buffers that they may need to do their individual processing. Depending on the system, it may have a specialized Update
, or Draw
method, or in some cases, both.
Interfaces
Interfaces are a key structure in my system. They are used to define what a System
can process, and what an Entity
is capable of. The Interfaces that are relevant for rendering are: IRenderable
and IAnimatable
.
The interfaces simply tell the system which components are available. For example, the rendering system needs to know the bounding box of the entity and the image to draw. In my case, that would be the SpatialComponent
and the ImageComponent
. So it looks like this:
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
The RenderingSystem
So how does the rendering system draw an entity? It's actually quite simple, so I'll just show you the stripped down class to give you an idea:
public class RenderSystem {
private SpriteBatch batch;
public RenderSystem(SpriteBatch batch) {
this.batch = batch;
}
public void Draw(List list) {
foreach(IRenderable obj in list) {
this.batch.draw(
obj.Image.Texture,
obj.Spatial.Position,
obj.Image.Source,
Color.White);
}
}
}
Looking at the class, the render system doesn't even know what an Entity
is. All it knows about is IRenderable
and it is simply given a list of them to draw.
How It All Works
It may help to also understand how I create new game objects and how I feed them to the systems.
Creating Entities
All game objects inherit from Entity, and any applicable interfaces that describe what that game object can do. Just about everything that is animated on screen looks like this:
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
Feeding the Systems
I keep a list of all entities that exist in the game world in a single list called List
. Each frame, I then sift through that list and copy object references to more lists based on interface type, such as List
, and List
. This way, if different systems need to process the same entity, they can. Then I simply hand those lists to each one of the systems Update
or Draw
methods and let the systems do their work.
Animation
You might be curious how the animation system works. In my case you may want to see the IAnimatable interface:
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
The key thing to notice here is the ImageComponent
aspect of the IAnimatable
interface is not read-only; it has a setter.
As you may have guessed, the animation component just holds data about the animation; a list of frames (which are image components), the current frame, the number of frames per second to be drawn, the elapsed time since the last frame increment, and other options.
The animation system takes advantage of the rendering system and image component relationship. It simply changes the image component of the entity as it increments the animation's frame. That way, the animation is rendered indirectly by the rendering system.
No comments:
Post a Comment