Questa è una domanda difficile a cui rispondere perché ognuno ha la propria idea su come dovrebbe essere strutturato un sistema di componenti di entità. Il meglio che posso fare è condividere con te alcune delle cose che ho trovato più utili per me.
Entità
Adotto l'approccio di classe grassa all'ECS, probabilmente perché trovo i metodi estremi di programmazione altamente inefficienti (in termini di produttività umana). A tal fine, un'entità per me è una classe astratta che deve essere ereditata da classi più specializzate. L'entità ha un numero di proprietà virtuali e un semplice flag che mi dice se questa entità dovrebbe esistere o meno. Quindi rispetto alla tua domanda su un sistema di rendering, ecco Entitycome appare:
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; }
}
componenti
I componenti sono "stupidi" in quanto non fanno o non sanno nulla. Non hanno riferimenti ad altri componenti e in genere non hanno funzioni (lavoro in C #, quindi utilizzo le proprietà per gestire getter / setter - se hanno funzioni, si basano sul recupero dei dati in loro possesso).
sistemi
I sistemi sono meno "stupidi", ma sono ancora automi stupidi. Non hanno contesto dell'intero sistema, non hanno riferimenti ad altri sistemi e non contengono dati se non per alcuni buffer di cui potrebbero aver bisogno per eseguire la loro elaborazione individuale. A seconda del sistema, può disporre di un metodo specializzato Updateo Draw, in alcuni casi, di entrambi.
interfacce
Le interfacce sono una struttura chiave nel mio sistema. Sono usati per definire ciò che un Systemprocesso può e di cosa Entityè capace. Le interfacce rilevanti per il rendering sono: IRenderablee IAnimatable.
Le interfacce indicano semplicemente al sistema quali componenti sono disponibili. Ad esempio, il sistema di rendering deve conoscere il rettangolo di selezione dell'entità e l'immagine da disegnare. Nel mio caso, quello sarebbe il SpatialComponente il ImageComponent. Quindi sembra così:
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
Il sistema di rendering
Quindi, come fa il sistema di rendering a disegnare un'entità? In realtà è abbastanza semplice, quindi ti mostrerò la lezione ridotta per darti un'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);
}
}
}
Guardando la classe, il sistema di rendering non sa nemmeno cosa Entitysia. Tutto quello che sa è IRenderablee viene semplicemente dato un elenco di loro da disegnare.
Come funziona tutto
Può aiutare a capire anche come creo nuovi oggetti di gioco e come li alimento ai sistemi.
Creazione di entità
Tutti gli oggetti di gioco ereditano da Entità e tutte le interfacce applicabili che descrivono cosa può fare quell'oggetto di gioco. Quasi tutto ciò che è animato sullo schermo è simile al seguente:
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
Nutrire i sistemi
Tengo un elenco di tutte le entità esistenti nel mondo di gioco in un unico elenco chiamato List<Entity> gameObjects. Ogni fotogramma, quindi setaccio l'elenco e copio i riferimenti agli oggetti in più elenchi in base al tipo di interfaccia, ad esempio List<IRenderable> renderableObjects, e List<IAnimatable> animatableObjects. In questo modo, se diversi sistemi devono elaborare la stessa entità, possono farlo. Quindi consegno semplicemente quegli elenchi a ciascuno dei sistemi Updateo Drawmetodi e lascio che i sistemi facciano il loro lavoro.
Animazione
Potresti essere curioso di sapere come funziona il sistema di animazione. Nel mio caso potresti voler vedere l'interfaccia IAnimatable:
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
La cosa fondamentale da notare qui è che l' ImageComponentaspetto IAnimatabledell'interfaccia non è di sola lettura; ha un setter .
Come avrai intuito, il componente animazione contiene solo dati sull'animazione; un elenco di fotogrammi (che sono componenti dell'immagine), il fotogramma corrente, il numero di fotogrammi al secondo da disegnare, il tempo trascorso dall'ultimo incremento del fotogramma e altre opzioni.
Il sistema di animazione sfrutta il sistema di rendering e la relazione tra i componenti dell'immagine. Cambia semplicemente il componente dell'immagine dell'entità mentre aumenta la cornice dell'animazione. In questo modo, l'animazione viene renderizzata indirettamente dal sistema di rendering.