Nachdem ich zwei Fragen zu Entitätssystemen ( 1 , 2 ) gestellt und einige Artikel darüber gelesen habe, denke ich, dass ich sie viel besser verstehe als zuvor. Ich habe immer noch einige Unsicherheiten, hauptsächlich im Hinblick auf den Bau eines Partikelemitters, eines Eingabesystems und einer Kamera. Ich habe natürlich immer noch einige Probleme beim Verstehen von Entitätssystemen und sie können auf eine ganze Reihe anderer Objekte angewendet werden, aber ich habe diese drei ausgewählt, weil sie sehr unterschiedliche Konzepte sind, einen ziemlich breiten Bereich abdecken und mir helfen, Entitätssysteme und deren Funktionsweise zu verstehen Behandle Probleme wie diese selbst, wenn sie auf dich zukommen.
Ich baue eine Engine in JavaScript und habe die meisten Kernfunktionen implementiert, darunter Eingabehandling, flexibles Animationssystem, Partikelemitter, mathematische Klassen und Funktionen, Szenenhandling, eine Kamera und ein Rendering sowie eine ganze Reihe von Funktionen von anderen Dingen, die Motoren normalerweise unterstützen. Ich habe die Antwort von Byte56 gelesen, die mich daran interessiert hat, die Engine zu einem Entitätssystem zu machen. Es würde immer noch eine HTML5-Spiel-Engine bleiben, mit der Grundphilosophie der Szene, aber es sollte die dynamische Erstellung von Entitäten aus Komponenten unterstützen.
Das Problem, das ich jetzt habe, besteht darin, mein altes Motorkonzept in dieses neue Programmierparadigma einzufügen. Dies sind einige der Definitionen aus den vorherigen Fragen, die aktualisiert wurden:
Eine Entität ist eine Kennung. Es hat keine Daten, es ist kein Objekt, es ist eine einfache ID, die einen Index in der Szenenliste aller Entitäten darstellt (die ich eigentlich als Komponentenmatrix implementieren möchte).
Eine Komponente ist ein Datenbehälter, jedoch mit Methoden, die diese Daten verarbeiten können. Das beste Beispiel ist eine
Vector2D
oder eine "Position" -Komponente. Es hat Daten:x
undy
, aber auch einige Methoden , die auf die Daten ein wenig einfacher machen die Bedienung:add()
,normalize()
, und so weiter.Ein System kann mit einer Reihe von Entitäten betrieben werden, die bestimmte Anforderungen erfüllen. In der Regel müssen die Entitäten über einen festgelegten Satz von Komponenten verfügen, um betrieben werden zu können. Das System ist der "Logik" -Teil, der "Algorithmus" -Teil, alle von den Komponenten bereitgestellten Funktionen dienen lediglich der einfacheren Datenverwaltung.
Kamera
Die Kamera verfügt über eine Vector2D
Positionseigenschaft, eine Rotationseigenschaft und einige Methoden zum Zentrieren um einen Punkt. Jedes Bild wird zusammen mit einer Szene einem Renderer zugeführt, und alle Objekte werden entsprechend ihrer Position verschoben. Die Szene wird dann gerendert.
Wie könnte ich ein solches Objekt in einem Entitätssystem darstellen? Wäre die Kamera eine Entität, eine Komponente oder eine Kombination (gemäß meiner Antwort )?
Teilchenemitter
Das Problem, das ich mit meinem Partikelemitter habe, ist wiederum, was was sein sollte. Ich bin mir ziemlich sicher, dass Partikel selbst keine Entitäten sein sollten, da ich über 10.000 von ihnen unterstützen möchte, und ich glaube, dass das Schaffen so vieler Entitäten meine Leistung stark beeinträchtigen würde.
Wie könnte ich ein solches Objekt in einem Entitätssystem darstellen?
Input Manager
Das letzte, worüber ich sprechen möchte, ist, wie mit Eingaben umgegangen werden soll. In meiner aktuellen Version der Engine gibt es eine Klasse namens Input
. Hierbei handelt es sich um einen Handler, der die Ereignisse des Browsers, z. B. das Drücken von Tasten und das Ändern der Mausposition, abonniert und außerdem einen internen Status beibehält. Die Player-Klasse verfügt dann über eine react()
Methode, die ein Eingabeobjekt als Argument akzeptiert. Dies hat den Vorteil, dass das Eingabeobjekt in .JSON serialisiert und dann über das Netzwerk gemeinsam genutzt werden kann, um reibungslose Mehrspielersimulationen zu ermöglichen.
Wie übersetzt sich dies in ein Entitätssystem?
Hier ist, wie ich das angegangen bin:
Kamera
Meine Kamera ist eine Entität wie jede andere, an die Komponenten angeschlossen sind:
Transform
hatTranslation
,Rotation
undScale
Eigenschaften, neben anderen für Geschwindigkeit usw.Pov
(Sicht) hatFieldOfView
,AspectRatio
,Near
,Far
, und alles , was erforderlich , um eine Projektionsmatrix zu erzeugen, zusätzlich zu einemIsOrtho
Flag , um zwischen Perspektive verwendet und orthogonalen Projektionen.Pov
Stellt außerdem eine Lazy-Load-ProjectionMatrix
Eigenschaft bereit, die vom Rendersystem verwendet wird und die beim Lesen intern berechnet und zwischengespeichert wird, bis eine der anderen Eigenschaften geändert wird.Es gibt kein spezielles Kamerasystem. Das Rendersystem verwaltet eine Liste von
Pov
und enthält eine Logik zum Bestimmen, welche beim Rendern verwendet werden soll.Eingang
Eine
InputReceiver
Komponente kann an eine beliebige Entität angehängt werden. Diesem ist ein Ereignishandler (oder Lambda, wenn Ihre Sprache dies unterstützt) angehängt, der zur Aufnahme der entitätsspezifischen Eingabeverarbeitung verwendet wird und Parameter für den aktuellen und vorherigen Tastenzustand, die aktuelle und vorherige Mausposition und den Tastenzustand usw. enthält. Es gibt separate Handler für Maus und Tastatur.Zum Beispiel habe ich in einem Asteroids-ähnlichen Testspiel, das ich erstellt habe, als ich mich an Entity / Component gewöhnt habe, zwei Lambda-Eingabemethoden. Die Schiffsnavigation erledigt man mit den Pfeiltasten und der Leertaste (zum Feuern). Die andere behandelt allgemeine Tastatureingaben - Tasten zum Beenden, Anhalten usw., Neustarten usw. Ich erstelle zwei Komponenten, ordne jedes Lambda einer eigenen Komponente zu und ordne dann die Navigationsempfängerkomponente der Schiffseinheit zu, die andere einer nicht sichtbare Befehlsprozessorentität.
Hier ist die Ereignisbehandlungsroutine zum Behandeln von Schlüsseln zwischen Frames, die an die Schiffskomponente angehängt
InputReceiver
werden (C #):Wenn Ihre Kamera mobil ist, geben Sie ihr eine eigene
InputReceiver
und eineTransform
Komponente, schließen Sie ein Lambda oder einen Handler an, der die gewünschte Steuerung implementiert, und fertig.Dies ist insofern eine nette
InputReceiver
Sache, als Sie die Komponente mit dem Navigations-Handler vom Schiff auf einen Asteroiden oder auf irgendetwas anderes verschieben und stattdessen herumfliegen können. Wenn Sie einePov
Komponente einem anderen Element in Ihrer Szene zuweisen - einem Asteroiden, einer Straßenlampe usw. -, können Sie Ihre Szene aus der Perspektive dieser Entität betrachten.Eine
InputSystem
Klasse, die einen internen Status für Tastatur, Maus usw. verwaltet,InputSystem
filtert ihre interne Entitätssammlung nach Entitäten mit einerInputReceiver
Komponente. In seinerUpdate()
Methode durchläuft es diese Auflistung und ruft die an jede dieser Komponenten angehängten Eingabehandler auf dieselbe Weise auf, wie das Rendering-System jede Entität mit einerRenderable
Komponente zeichnet .Teilchen
Dies hängt wirklich davon ab, wie Sie mit den Partikeln interagieren möchten. Wenn Sie nur ein Partikelsystem benötigen, das sich wie ein Objekt verhält - beispielsweise ein Feuerwerk, das anzeigt, dass der Spieler weder berühren noch schlagen kann -, erstellen Sie eine einzelne Entität und eine
ParticleRenderGroup
Komponente, die alle Informationen enthält, die Sie für die Partikel benötigen. Zerfall usw. - das wird von IhrerRenderable
Komponente nicht abgedeckt . Beim Rendern würde das Rendersystem sehen, ob eine Entität dieRenderParticleGroup
angehängte hat, und es entsprechend behandeln.Wenn Sie einzelne Partikel benötigen, um an der Kollisionserkennung teilzunehmen, auf Eingaben zu reagieren usw., diese jedoch nur als Stapel wiedergeben möchten, erstellen Sie eine
Particle
Komponente, die diese Informationen auf Partikelbasis enthält, und erstellen Sie sie als getrennte Einheiten. Das Rendersystem kann sie weiterhin stapeln, sie werden jedoch von den anderen Systemen als separate Objekte behandelt. (Dies funktioniert sehr gut mit Instanzen.)MotionSystem
Führen Sie dann entweder in Ihrem (oder einem anderen, der die Aktualisierung der Entitätsposition übernimmt usw.) oder in einem dediziertenParticleSystem
Modus die für jedes Partikel pro Frame erforderliche Verarbeitung durch. DasRenderSystem
wäre verantwortlich für den Aufbau / Dosier- und Caching von Partikel Sammlungen wie sie erzeugt und zerstört, und machen sie je nach Bedarf.Eine schöne Sache bei diesem Ansatz ist, dass Sie keine speziellen Fälle für Kollisionen, Keulen usw. für Partikel haben müssen. Der Code, den Sie für jede andere Art von Entität schreiben, kann weiterhin verwendet werden.
Fazit
Wenn Sie überlegen, plattformübergreifend zu arbeiten, was für JavaScript nicht besonders geeignet ist, wird Ihr plattformspezifischer Code (Rendering und Eingabe) auf zwei Systeme aufgeteilt. Ihre Spielelogik bleibt in plattformunabhängigen Klassen (Bewegung, Kollision usw.), sodass Sie sie beim Portieren nicht berühren müssen.
Ich verstehe und stimme mit Seans Position überein, dass es schlecht ist, Dinge mit Schuhen in ein Muster zu integrieren, um sich strikt an das Muster zu halten, anstatt das Muster zu optimieren, um die Anforderungen Ihrer Anwendung zu erfüllen. Ich sehe einfach nichts in Input, Kamera oder Partikeln, was eine solche Behandlung erfordert.
quelle
Eingabe- und Spielelogik werden wahrscheinlich in einem dedizierten Codeabschnitt außerhalb des Entitätskomponentensystems verarbeitet. Es ist technisch möglich, es in das Design einzufügen, aber es hat wenig Vorteile - die Spielelogik und die Benutzeroberfläche sind hackig und voller undichter Abstraktionen, egal was Sie tun, und der Versuch, den quadratischen Stift nur aus Gründen der architektonischen Reinheit in ein rundes Loch zu zwingen, ist eine Verschwendung von Zeit.
Ebenso sind Partikelemitter besondere Biester, besonders wenn es Ihnen um Leistung geht. Eine Emitter-Komponente ist sinnvoll, aber Grafiken werden mit diesen Komponenten eine besondere Magie vollbringen, die für den Rest des Renderns mit der Magie vermischt wird.
Geben Sie in Bezug auf Ihre Kamera einfach eine aktive Markierung und möglicherweise einen Tiefenindex an, und lassen Sie das Grafiksystem alle aktivierten rendern. Dies ist praktisch für viele Tricks, einschließlich GUIs (möchten Sie, dass Ihre GUI in einem orthografischen Modus über der Spielwelt gerendert wird? Kein Problem, es sind nur zwei Kameras mit unterschiedlichen Objektmasken und GUI auf einer höheren Ebene). Es ist auch nützlich für Spezialeffektebenen und dergleichen.
quelle
Ich bin mir nicht sicher, was diese Frage wirklich stellt. Da das einzige, was Sie im Spiel haben, Objekte sind, müssen Kameras Objekte sein. Die Kamerafunktionalität wird über eine Art Kamerakomponente implementiert. Keine separaten Komponenten "Position" und "Rotation" - das ist viel zu niedrig. Sie sollten zu einer Art WorldPosition-Komponente zusammengefasst werden, die für jede Entität auf der Welt gilt. Was man verwenden soll ... man muss irgendwie Logik in das System bringen. Entweder Sie codieren es fest in Ihr Kamera-Handlingsystem oder Sie hängen Skripte an oder so. Sie können ein aktiviertes / deaktiviertes Flag für eine Kamerakomponente festlegen, wenn dies hilfreich ist.
Ich auch. Ein Partikelemitter wäre eine Entität, und das Partikelsystem würde die Partikel verfolgen, die einer bestimmten Entität zugeordnet sind. In solchen Dingen merkt man, dass "alles eine Einheit ist" absurd unpraktisch ist. In der Praxis sind die einzigen Entitäten relativ komplexe Objekte, die von der Kombination von Komponenten profitieren.
Was Input betrifft: Input existiert in der Spielwelt nicht als solcher, so dass dies von einem System gehandhabt wird. Nicht unbedingt ein 'Komponentensystem', da sich nicht alles in Ihrem Spiel um Komponenten dreht. Es wird aber ein Eingabesystem geben. Möglicherweise möchten Sie die Entität, die auf Eingaben reagiert, mit einer Art Player-Komponente kennzeichnen. Die Eingabe wird jedoch komplex und vollständig spielspezifisch sein, sodass es wenig Sinn macht, Komponenten dafür zu erstellen.
quelle
Hier sind einige meiner Ideen zur Lösung dieser Probleme. Sie werden wahrscheinlich etwas falsch mit ihnen haben, und es wird wahrscheinlich einen besseren Ansatz geben. Bitte leiten Sie mich zu denen in Ihrer Antwort!
Kamera :
Es gibt eine "Kamera" -Komponente, die zu jeder Entität hinzugefügt werden kann. Ich kann mir allerdings nicht wirklich vorstellen, welche Daten ich in diese Komponente einfügen soll: Ich könnte getrennte "Position" - und "Rotation" -Komponenten haben! Die
follow
Methode muss nicht implementiert werden, da sie bereits der Entität folgt, an die sie angehängt ist! Und ich kann es frei bewegen. Das Problem mit diesem System wären viele verschiedene Kameraobjekte: Wie können dieRendererSystem
wissen, welche zu verwenden sind? Früher habe ich das Kameraobjekt nur herumgereicht, aber jetzt scheint es so, alsRendererSystem
müsste das Objekt zweimal über alle Entitäten iteriert werden: erstens, um diejenigen zu finden, die sich wie Kameras verhalten, und zweitens, um tatsächlich alles zu rendern.PartikelEmitter :
Es würde eine geben,
ParticleSystem
die alle Entitäten aktualisieren würde, die eine "Emitter" -Komponente hatten. Teilchen sind stumme Objekte in einem relativen Koordinatenraum innerhalb dieser Komponente. Hier gibt es ein Problem beim Rendern: Ich müsste entweder einParticleRenderer
System erstellen oder die Funktionalität des vorhandenen Systems erweitern.Eingabesystem :
Das Hauptanliegen für mich war hier die Logik oder die
react()
Methode. Die einzige Lösung, die ich gefunden habe, ist ein separates System und eine Komponente für jedes System, die angibt, welches verwendet werden soll. Das scheint wirklich zu abgedreht zu sein und ich weiß nicht, wie ich damit umgehen soll. Eine Sache ist, dass, solange es mich betrifft, dasInput
als Klasse implementiert bleiben kann, aber ich sehe nicht, wie ich es in den Rest des Spiels integrieren könnte.quelle