Hinweis: Ich programmiere dies in Javascript, aber es sollte größtenteils sprachunabhängig sein.
Ich denke darüber nach, meinen Motor auf einen ECS-basierten umzustellen.
Ich habe die Grundidee ( Hinweis: Das ist falsch, siehe meine Antwort ):
Entitäten sind Spielobjekte.
Komponenten sind Teile der Funktionalität ( reactToInput()
) oder des Zustands ( position
), die an Entitäten "geklebt" werden können.
Systeme haben eine Liste von Entitäten, die sie verwalten und aktualisieren.
Aber ich bin mir nicht ganz sicher, ob ich die Implementierung und einige Details bekomme ...
Frage: Kann ein System mit verschiedenen Arten von Entitäten arbeiten? Normalerweise gebe ich das Beispiel einer Klasse an, die Scene
in meiner Engine aufgerufen wird , und sie wird jetzt auch diesem Zweck dienen. Eine Szene ist ein Container aller Objekte, die gerendert, aktualisiert, das Rendering (Lichter) und möglicherweise in Zukunft sogar 2DSoundEmitter
Objekte beeinflusst werden können. Es verfügt über eine übergeordnete Benutzeroberfläche, sodass sich der Benutzer nicht um den Typ des Objekts scene.add()
und all diese Dinge kümmern muss .
Mir ist klar, dass das Scene
ein System sein könnte. Es nimmt Entitäten auf, speichert sie und kann dann ihre Aktualisierungsmethoden aufrufen und möglicherweise sogar einige Statusänderungen vornehmen. Aber es gibt ein Problem: Wie ich oben beschrieben habe, Scene
können verschiedene Arten von Objekten gefüttert werden ! Was soll ich beispielsweise in einer Situation tun, in der eine Szene sowohl renderbare Objekte ("Drawables") als auch Lichter enthält? Sollte ich Entitäten vor der Interaktion typüberprüfen lassen? Oder sollte ich es auf einer noch niedrigeren Ebene lösen: Erstellen Sie eine LightSource
Komponente, die zu jedem Objekt hinzugefügt werden kann, und das Licht wäre nur eine Einheit mit LightSource
und Position
Komponenten. Ist das akzeptabel?
Ist es auch eine gute Praxis, weiterhin konventionelle Vererbung und traditionelle Klassen zu verwenden? Zum Beispiel kann ich einfach nicht herausfinden, was mein Renderer
wäre! Es ist kein System, da seine einzige Funktion darin besteht, eine Kamera und eine Szene aufzunehmen, alles zu rendern und Effekte (wie Schatten) anzuwenden. Es verwaltet auch den Kontext, die Breite und die Höhe des Spiels, macht Übersetzungen ... Aber es ist immer noch kein System!
Bearbeiten: Könnten Sie vielleicht Ressourcen verknüpfen, die Sie im ECS gefunden haben? Ich habe Probleme, gute zu finden.
Antworten:
Lassen Sie mich sehen, ob ich durch den Versuch, als Web- / UI-JS-Entwickler zu verstehen, hilfreich sein kann. Gehen Sie auch nicht zu weit in der Sprachunabhängigkeit. Viele Muster, die in anderen Sprachen erstellt wurden, sind es wert, studiert zu werden, können jedoch aufgrund ihrer Flexibilität in JS sehr unterschiedlich angewendet werden oder sind aufgrund der Formbarkeit der Sprache nicht unbedingt erforderlich. Sie könnten einige Möglichkeiten nutzen, wenn Sie Ihren Code so schreiben, dass JS dieselben Grenzen hat wie eine klassischere OOP-orientierte Sprache.
Denken Sie beim Faktor "OOP nicht verwenden" zunächst daran, dass JavaScript-Objekte im Vergleich zu anderen Sprachen wie Knetmasse sind und Sie sich wirklich alle Mühe geben müssen, um einen Alptraum für ein Kaskadenvererbungsschema zu erstellen, da JS keine Klasse ist -basiert und Compositing kommt viel natürlicher dazu. Wenn Sie ein albernes Klassen- oder Prototyp-Hand-Me-Down-System in Ihrem JS implementieren, sollten Sie es fallen lassen. In JS verwenden wir Verschlüsse, Prototypen und geben Funktionen wie Süßigkeiten weiter. Es ist widerlich und schmutzig und falsch, aber auch kraftvoll, prägnant und so mögen wir es.
Vererbungsintensive Ansätze werden in Design Patterns tatsächlich als Anti-Pattern formuliert, und das aus gutem Grund für alle, die Klassen oder klassenähnliche Strukturen im Wert von mehr als 15 Stufen durchlaufen haben, um herauszufinden, wo zum Teufel die kaputte Version einer Methode ist kam herein von kann dir sagen.
Ich weiß nicht, warum so viele Programmierer dies lieben (besonders Java-Leute, die aus irgendeinem Grund JavaScript schreiben), aber es ist schrecklich, unleserlich und völlig unbrauchbar, wenn man es an Übermaß gewöhnt. Vererbung ist hier und da in Ordnung, aber in JS nicht wirklich notwendig. In Sprachen, in denen es sich um eine verlockendere Abkürzung handelt, sollte sie eher abstrakteren Architekturproblemen vorbehalten sein als wörtlicheren Modellierungsschemata wie dem Frankensteining einer Zombie-Implementierung durch eine Vererbungskette, die ein BunnyRabbit enthielt, weil es zufällig funktionierte. Das ist keine gute Wiederverwendung von Code. Es ist ein Alptraum für die Instandhaltung.
Als JS-Entwickler erscheinen mir Entity / Component / System-basierte Engines als System / Muster, um Designprobleme zu entkoppeln und dann Objekte für die Implementierung auf einer sehr detaillierten Ebene zusammenzusetzen. Mit anderen Worten, ein Kinderspiel in einer Sprache wie JavaScript. Aber lassen Sie mich sehen, ob ich das zuerst richtig mache.
Entität - Die spezifische Sache, die Sie entwerfen. Wir sprechen mehr in Richtung der Eigennamen (aber natürlich nicht wirklich). Nicht 'Scene', sondern 'IntroAreaLevelOne'. IntroAreaLevelOne befindet sich möglicherweise in einer SzeneEntity-Box, aber wir konzentrieren uns auf etwas Bestimmtes, das sich von anderen verwandten Dingen unterscheidet. Im Code ist eine Entität eigentlich nur ein Name (oder eine ID), der an eine Reihe von Dingen gebunden ist, die implementiert oder eingerichtet werden müssen (die Komponenten), um nützlich zu sein.
Komponenten - Arten von Dingen, die eine Entität benötigt. Dies sind allgemeine Substantive. Wie WalkingAnimation. Innerhalb von WalkingAnimation können wir spezifischer werden, wie "Shambling" (gute Wahl für Zombies und Pflanzenmonster) oder "ChickenWalker" (ideal für Robotertypen mit umgekehrten Gelenken). Hinweis: Ich bin mir nicht sicher, wie sich das vom Rendern eines solchen 3D-Modells abkoppeln könnte - also vielleicht ein Mistbeispiel, aber ich bin eher ein JS-Profi als ein erfahrener Spieleentwickler. In JS würde ich den Mapping-Mechanismus mit den Komponenten in dieselbe Box setzen. Eigene Komponenten sind wahrscheinlich logisch und eher eine Roadmap, die Ihren Systemen sagt, was zu implementieren ist, wenn Systeme überhaupt benötigt werden (bei meinem Versuch, ECS zu verwenden, sind einige Komponenten nur Sammlungen von Eigenschaftssätzen). Sobald eine Komponente eingerichtet ist, wird sie
Systeme - Das echte Programmfleisch ist da. KI-Systeme werden gebaut und verknüpft, Rendering wird erreicht, Animationssequenzen werden erstellt usw. Ich überlasse diese und überlasse sie hauptsächlich der Vorstellungskraft, aber im Beispiel System.AI nimmt eine Reihe von Eigenschaften auf und spuckt eine Funktion aus, die wird verwendet, um dem Objekt Ereignishandler hinzuzufügen, die letztendlich in der Implementierung verwendet werden. Das Wichtigste an System.AI ist, dass es mehrere Komponententypen abdeckt. Sie könnten alle KI-Dinge mit einer Komponente aussortieren, aber dies zu tun, bedeutet, den Sinn der Granularisierung der Dinge falsch zu verstehen.
Beachten Sie die Ziele: Wir möchten es Nicht-Designern einfach machen, eine Art GUI-Oberfläche einzubinden, um verschiedene Arten von Dingen zu optimieren, indem wir Komponenten innerhalb eines für sie sinnvollen Paradigmas maximieren und aufeinander abstimmen, und wir möchten davon wegkommen Beliebte beliebige Codeschemata, die viel einfacher zu schreiben sind als zu ändern oder zu warten.
Also in JS vielleicht so etwas. Spielentwickler sagen mir bitte, wenn ich es schrecklich falsch verstanden habe:
Jetzt können Sie jederzeit mit einem NPC bauen
npcBuilders.<npcName>();
Eine grafische Benutzeroberfläche kann in die Objekte npcEntities und Komponenten eingebunden werden und es Designern ermöglichen, alte Entitäten zu optimieren oder neue Entitäten zu erstellen, indem sie einfach Komponenten mischen und abgleichen (obwohl es dort keinen Mechanismus für nicht standardmäßige Komponenten gibt, sondern spezielle Komponenten im laufenden Betrieb hinzugefügt werden könnten Code, solange es eine definierte Komponente dafür gab.
quelle
Ich habe Entity Systems in den Artikeln gelesen, die freundliche Leute in den Kommentaren zur Verfügung gestellt haben, aber ich hatte immer noch einige Zweifel, also stellte ich eine andere Frage .
Zunächst einmal waren meine Definitionen falsch. Entitäten und Komponenten sind nur dumme Datenhalter, während Systeme alle Funktionen bereitstellen .
Ich habe genug gelernt, um den größten Teil meiner Frage hier zu behandeln, also werde ich sie beantworten.
Die
Scene
Klasse, über die ich gesprochen habe, sollte kein System sein. Es sollte jedoch ein zentraler Manager sein, der alle Entitäten enthalten, Nachrichten erleichtern und möglicherweise sogar Systeme verwalten kann. Es kann auch als eine Art Fabrik für Entitäten fungieren, und ich habe beschlossen, es so zu verwenden. Es kann jeden Entitätstyp annehmen, muss diese Entität dann jedoch einem geeigneten System zuführen (das aus Leistungsgründen keine Typprüfungen durchführen sollte, es sei denn, sie sind bitweise).Ich sollte bei der Implementierung eines ES kein OOP verwenden, schlägt Adam vor, aber ich finde keinen Grund, keine Objekte mit Methoden für Entitäten und Komponenten zu haben, nicht nur dumme Dateninhaber.
Das
Renderer
kann einfach als System implementiert werden. Es würde eine Liste von zeichnbaren Objekten führen und diedraw()
Methode ihrer Renderkomponente alle 16 ms aufrufen.quelle
Einführung in das Abhängigkeitsmanagement 101.
In diesem Kurs wird davon ausgegangen, dass Sie über Grundkenntnisse in Abhängigkeitsinjektion und Repository-Design verfügen.
Die Abhängigkeitsinjektion ist nur eine ausgefallene Möglichkeit für Objekte, miteinander zu sprechen (über Nachrichten / Signale / Delegierte / was auch immer), ohne direkt gekoppelt zu sein.
Es geht um den Satz: "
new
ist Kleber."Ich werde dies in C # demonstrieren.
Dies ist ein Basissystem für Eingabe, Bewegung und Rendern. Die
Player
Klasse ist in diesem Fall die Entität und die Komponenten sind die Schnittstellen. DiePlayer
Klasse weiß nichts über die Interna, wie eine konkreteIRenderSystem
,IMovementSystem
oderIInputSystem
Arbeit. Die Schnittstellen bieten jedoch die MöglichkeitPlayer
, Signale zu senden (z. B. Draw auf dem IRenderSystem aufrufen), ohne davon abhängig zu sein, wie das Endergebnis erzielt wird.Nehmen Sie zum Beispiel meine Implementierung von IMovementSystem:
MovementSystem
kann seine eigenen Abhängigkeiten haben und dasPlayer
würde nicht einmal interessieren. Mithilfe von Schnittstellen kann eine Spielstatusmaschine erstellt werden:Und das ist der Beginn eines schönen Spiels (das auch auf Einheiten getestet werden kann).
Und mit ein paar Ergänzungen und etwas Polymorphismus:
Wir haben jetzt ein primitives Entitäts- / Komponentensystem, das auf aggregierten Schnittstellen und loser Vererbung basiert.
quelle