Diese Frage ist schwer zu beantworten, da jeder seine eigene Vorstellung davon hat, wie ein Entitätskomponentensystem aufgebaut sein sollte. Das Beste, was ich tun kann, ist, Ihnen einige der Dinge mitzuteilen, die sich für mich als am nützlichsten erwiesen haben.
Entität
Ich verfolge den Fat-Class-Ansatz für ECS, wahrscheinlich weil ich extreme Programmiermethoden als äußerst ineffizient empfinde (in Bezug auf die menschliche Produktivität). Zu diesem Zweck ist eine Entität für mich eine abstrakte Klasse, die von spezialisierteren Klassen geerbt wird. Die Entität verfügt über eine Reihe virtueller Eigenschaften und ein einfaches Flag, das angibt, ob diese Entität vorhanden sein soll oder nicht. In Bezug auf Ihre Frage zu einem Render-System Entitysieht das also so aus:
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; }
}
Komponenten
Komponenten sind insofern "dumm", als sie nichts tun oder wissen . Sie haben keine Verweise auf andere Komponenten und normalerweise keine Funktionen (ich arbeite in C #, daher verwende ich Eigenschaften, um Getter / Setter zu behandeln - wenn sie Funktionen haben, basieren sie auf dem Abrufen von Daten, die sie enthalten).
Systeme
Systeme sind weniger "dumm", aber immer noch dumme Automaten. Sie haben keinen Kontext des Gesamtsystems, keine Verweise auf andere Systeme und enthalten keine Daten außer einigen Puffern, die sie möglicherweise für ihre individuelle Verarbeitung benötigen. Je nach System kann es eine spezielle Methode Updateoder DrawMethode oder in einigen Fällen beides geben.
Schnittstellen
Schnittstellen sind eine Schlüsselstruktur in meinem System. Sie werden verwendet, um zu definieren, was eine SystemDose verarbeiten kann und wozu eine Entityfähig ist. Die für das Rendern relevanten Schnittstellen sind: IRenderableund IAnimatable.
Die Schnittstellen teilen dem System einfach mit, welche Komponenten verfügbar sind. Beispielsweise muss das Rendering-System den Begrenzungsrahmen der Entität und das zu zeichnende Bild kennen. In meinem Fall wäre das das SpatialComponentund das ImageComponent. So sieht es aus:
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
Das RenderingSystem
Wie zeichnet das Rendering-System eine Entität? Es ist eigentlich ganz einfach, also zeige ich Ihnen nur die abgespeckte Klasse, um Ihnen eine Idee zu geben:
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);
}
}
}
Wenn man sich die Klasse ansieht, weiß das Render-System nicht einmal, was ein Entityist. Alles, was es weiß, ist IRenderableund es wird einfach eine Liste von ihnen zum Zeichnen gegeben.
Wie alles funktioniert
Es kann hilfreich sein, auch zu verstehen, wie ich neue Spielobjekte erstelle und wie ich sie den Systemen zuführe.
Entitäten erstellen
Alle Spielobjekte erben von Entity und alle anwendbaren Schnittstellen, die beschreiben, was dieses Spielobjekt tun kann. Fast alles, was auf dem Bildschirm animiert wird, sieht folgendermaßen aus:
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
Fütterung der Systeme
Ich führe eine Liste aller Entitäten, die in der Spielwelt existieren, in einer einzigen Liste mit dem Namen List<Entity> gameObjects. In jedem Frame durchsuche ich dann diese Liste und kopiere Objektreferenzen in weitere Listen, die auf dem Schnittstellentyp basieren, wie z. B. List<IRenderable> renderableObjectsund List<IAnimatable> animatableObjects. Auf diese Weise können verschiedene Systeme, die dieselbe Entität verarbeiten müssen, dies tun. Dann übergebe ich diese Listen einfach jedem der Systeme Updateoder DrawMethoden und lasse die Systeme ihre Arbeit machen.
Animation
Sie könnten neugierig sein, wie das Animationssystem funktioniert. In meinem Fall möchten Sie möglicherweise die IAnimatable-Oberfläche sehen:
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
Das Wichtigste dabei ist, dass der ImageComponentAspekt der IAnimatableBenutzeroberfläche nicht schreibgeschützt ist. es hat einen Setter .
Wie Sie vielleicht erraten haben, enthält die Animationskomponente nur Daten über die Animation. Eine Liste der Bilder (die Bildkomponenten sind), das aktuelle Bild, die Anzahl der zu zeichnenden Bilder pro Sekunde, die seit dem letzten Bildinkrement verstrichene Zeit und andere Optionen.
Das Animationssystem nutzt die Beziehung zwischen Rendering-System und Bildkomponente. Es ändert einfach die Bildkomponente der Entität, während der Rahmen der Animation erhöht wird. Auf diese Weise wird die Animation indirekt vom Rendering-System gerendert.