Esta es una pregunta difícil de responder porque todos tienen su propia idea sobre cómo debe estructurarse un sistema de componentes de la entidad. Lo mejor que puedo hacer es compartir con ustedes algunas de las cosas que me han resultado más útiles.
Entidad
Tomo el enfoque de clase gorda de ECS, probablemente porque encuentro que los métodos extremos de programación son altamente ineficientes (en términos de productividad humana). Para ese fin, una entidad para mí es una clase abstracta que heredarán clases más especializadas. La entidad tiene varias propiedades virtuales y un indicador simple que me dice si esta entidad debería existir o no. Entonces, en relación con su pregunta sobre un sistema de renderizado, esto es lo que Entity
parece:
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; }
}
Componentes
Los componentes son "estúpidos" porque no hacen ni saben nada. No tienen referencias a otros componentes y, por lo general, no tienen funciones (trabajo en C #, por lo que utilizo propiedades para manejar getters / setters; si tienen funciones, se basan en la recuperación de datos que tienen).
Sistemas
Los sistemas son menos "estúpidos", pero siguen siendo autómatas tontos. No tienen contexto del sistema en general, no tienen referencias a otros sistemas y no tienen datos, excepto por algunos buffers que pueden necesitar para su procesamiento individual. Dependiendo del sistema, que puede tener un especializado Update
, o Draw
método, o en algunos casos, ambas cosas.
Interfaces
Las interfaces son una estructura clave en mi sistema. Se utilizan para definir qué System
puede procesar y de qué Entity
es capaz. Las interfaces que son relevantes para la representación son: IRenderable
y IAnimatable
.
Las interfaces simplemente le dicen al sistema qué componentes están disponibles. Por ejemplo, el sistema de representación necesita conocer el cuadro delimitador de la entidad y la imagen a dibujar. En mi caso, ese sería el SpatialComponent
y el ImageComponent
. Entonces se ve así:
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
El sistema de renderizado
Entonces, ¿cómo dibuja el sistema de representación una entidad? En realidad es bastante simple, así que solo te mostraré la clase simplificada para darte una idea:
public class RenderSystem {
private SpriteBatch batch;
public RenderSystem(SpriteBatch batch) {
this.batch = batch;
}
public void Draw(List<IRenderable> list) {
foreach(IRenderable obj in list) {
this.batch.draw(
obj.Image.Texture,
obj.Spatial.Position,
obj.Image.Source,
Color.White);
}
}
}
Mirando la clase, el sistema de renderizado ni siquiera sabe qué Entity
es un . Todo lo que sabe es IRenderable
y simplemente se le da una lista de ellos para dibujar.
Cómo funciona todo
Puede ser útil comprender también cómo creo nuevos objetos de juego y cómo los alimento a los sistemas.
Crear entidades
Todos los objetos del juego heredan de Entity, y cualquier interfaz aplicable que describa lo que ese objeto del juego puede hacer. Casi todo lo que se anima en la pantalla se ve así:
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
Alimentando los sistemas
Mantengo una lista de todas las entidades que existen en el mundo del juego en una sola lista llamada List<Entity> gameObjects
. Cada cuadro, luego examino esa lista y copio referencias de objetos a más listas basadas en el tipo de interfaz, como List<IRenderable> renderableObjects
, y List<IAnimatable> animatableObjects
. De esta manera, si diferentes sistemas necesitan procesar la misma entidad, pueden hacerlo. Luego simplemente entrego esas listas a cada uno de los sistemas Update
o Draw
métodos y dejo que los sistemas hagan su trabajo.
Animación
Quizás tengas curiosidad por saber cómo funciona el sistema de animación. En mi caso, es posible que desee ver la interfaz IAnimatable:
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
La clave para notar aquí es que el ImageComponent
aspecto de la IAnimatable
interfaz no es de solo lectura; Tiene un setter .
Como habrás adivinado, el componente de animación solo contiene datos sobre la animación; una lista de fotogramas (que son componentes de imagen), el fotograma actual, el número de fotogramas por segundo que se dibujarán, el tiempo transcurrido desde el último incremento de fotogramas y otras opciones.
El sistema de animación aprovecha el sistema de representación y la relación del componente de imagen. Simplemente cambia el componente de imagen de la entidad a medida que incrementa el marco de la animación. De esa manera, la animación se representa indirectamente por el sistema de representación.