Okey, was ich bisher weiß; Die Entität enthält eine Komponente (Datenspeicherung), die Informationen wie enthält; - Textur / Sprite - Shader - etc.
Und dann habe ich ein Renderer-System, das all dies zeichnet. Was ich aber nicht verstehe, ist, wie der Renderer gestaltet werden soll. Sollte ich eine Komponente für jeden "visuellen Typ" haben? Eine Komponente ohne Shader, eine mit Shader usw.?
Benötigen Sie nur einige Eingaben, was der "richtige Weg" ist, um dies zu tun. Tipps und Fallstricke, auf die Sie achten sollten.
Antworten:
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
Entity
sieht das also so aus: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
Update
oderDraw
Methode oder in einigen Fällen beides geben.Schnittstellen
Schnittstellen sind eine Schlüsselstruktur in meinem System. Sie werden verwendet, um zu definieren, was eine
System
Dose verarbeiten kann und wozu eineEntity
fähig ist. Die für das Rendern relevanten Schnittstellen sind:IRenderable
undIAnimatable
.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
SpatialComponent
und dasImageComponent
. So sieht es aus: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:
Wenn man sich die Klasse ansieht, weiß das Render-System nicht einmal, was ein
Entity
ist. Alles, was es weiß, istIRenderable
und 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:
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> renderableObjects
undList<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 SystemeUpdate
oderDraw
Methoden 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:
Das Wichtigste dabei ist, dass der
ImageComponent
Aspekt derIAnimatable
Benutzeroberflä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.
quelle
Sehen Sie sich diese Antwort an, um zu sehen, um welches System es sich handelt.
Die Komponente sollte die Details enthalten, was und wie gezeichnet werden soll. Das Rendering-System nimmt diese Details und zeichnet die Entität auf die von der Komponente angegebene Weise. Nur wenn Sie erheblich unterschiedliche Zeichentechnologien verwenden würden, hätten Sie separate Komponenten für separate Stile.
quelle
Der Hauptgrund für die Aufteilung der Logik in Komponenten besteht darin, einen Datensatz zu erstellen, der in einer Entität ein nützliches, wiederverwendbares Verhalten erzeugt. Das Trennen einer Entität in eine PhysicsComponent und eine RenderComponent ist beispielsweise sinnvoll, da wahrscheinlich nicht alle Entitäten über Physik verfügen und einige Entitäten möglicherweise nicht über Sprite verfügen.
Um Ihre Frage zu beantworten, müssen Sie sich Ihre Architektur ansehen und sich zwei Fragen stellen:
Wenn Sie eine Komponente aufteilen, ist es wichtig, diese Frage zu stellen. Wenn die Antwort auf 1. Ja lautet, haben Sie wahrscheinlich einen guten Kandidaten für die Erstellung von zwei separaten Komponenten, eine mit einem Shader und eine mit Textur. Die Antwort auf 2. lautet normalerweise Ja für Komponenten wie Position, bei denen mehrere Komponenten die Position verwenden können.
Beispielsweise können sowohl Physik als auch Audio dieselbe Position verwenden, anstatt dass beide Komponenten doppelte Positionen speichern, die Sie in eine PositionComponent umgestalten, und erfordern, dass Entitäten, die PhysicsComponent / AudioComponent verwenden, auch eine PositionComponent haben.
Basierend auf den Informationen, die Sie uns gegeben haben, scheint Ihre RenderComponent kein guter Kandidat für die Aufteilung in eine TextureComponent und eine ShaderComponent zu sein, da Shader vollständig von Texture und nichts anderem abhängig sind.
Angenommen, Sie verwenden etwas Ähnliches wie T-Machine: Entity Systems. Eine Beispielimplementierung einer RenderComponent & RenderSystem in C ++ würde ungefähr so aussehen:
quelle
Fallstrick Nr. 1: Überentwickelter Code. Überlegen Sie, ob Sie wirklich alles brauchen, was Sie implementieren, weil Sie einige Zeit damit leben müssen.
Fallstrick Nr. 2: zu viele Objekte. Ich würde kein System mit zu vielen Objekten verwenden (eines für jeden Typ, Subtyp und was auch immer), da dies die automatisierte Verarbeitung nur erschwert. Meiner Meinung nach ist es viel schöner, wenn jedes Objekt einen bestimmten Funktionsumfang steuert (im Gegensatz zu einem Funktionsumfang). Zum Beispiel ist das Erstellen von Komponenten für jedes im Rendering enthaltene Datenbit (Texturkomponente, Shader-Komponente) zu geteilt - normalerweise müssten Sie sowieso alle diese Komponenten zusammen haben, würden Sie nicht zustimmen?
Fallstrick Nr. 3: zu strenge externe Kontrolle. Ändern Sie lieber Namen als Shader- / Texturobjekte, da sich Objekte mit Renderer / Textur-Typ / Shader-Format / was auch immer ändern können. Namen sind einfache Bezeichner - es liegt am Renderer, zu entscheiden, was daraus gemacht werden soll. Eines Tages möchten Sie vielleicht Materialien anstelle von einfachen Shadern haben (fügen Sie beispielsweise Shader, Texturen und Mischmodi aus Daten hinzu). Mit einer textbasierten Oberfläche ist es viel einfacher, dies zu implementieren.
Der Renderer kann eine einfache Schnittstelle sein, die von Komponenten erstellte Objekte erstellt / zerstört / verwaltet / rendert. Die primitivste Darstellung davon könnte ungefähr so sein:
Auf diese Weise können Sie diese Objekte von Ihren Komponenten aus verwalten und so weit halten, dass Sie sie nach Ihren Wünschen rendern können.
quelle