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 Entity
come 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 Update
o Draw
, in alcuni casi, di entrambi.
interfacce
Le interfacce sono una struttura chiave nel mio sistema. Sono usati per definire ciò che un System
processo può e di cosa Entity
è capace. Le interfacce rilevanti per il rendering sono: IRenderable
e 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 SpatialComponent
e 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 Entity
sia. Tutto quello che sa è IRenderable
e 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 Update
o Draw
metodi 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' ImageComponent
aspetto IAnimatable
dell'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.