Was ist eine gute Entitätshierarchie für ein 2D-Spiel?

7

Ich bin gerade dabei, ein neues 2D-Spiel aus einem Code zu erstellen, den ich vor einiger Zeit geschrieben habe.

Die Objekthierarchie für Entitäten lautet wie folgt:

  • Scene(zB MainMenu): Enthält mehrere Entitäten und Delegierte update()/ draw()an jede
  • Entity: Basisklasse für alle Dinge in einer Szene (zB MenuItemoder Alien)
  • Sprite: Basisklasse für alle Entitäten, die nur eine Textur zeichnen, dh keine eigene Zeichenlogik haben

Ist es sinnvoll, Entitäten und Sprites so aufzuteilen? Ich denke in einem 2D-Spiel sind die Begriffe Entität und Sprite etwas synonym, oder?

Ich glaube jedoch, dass ich eine Basisklasse für Entitäten benötige, die nur eine Textur zeichnen, anstatt sich selbst zu zeichnen, um Doppelarbeit zu vermeiden. Die meisten Entitäten sind so.

Ein seltsamer Fall ist meine TextKlasse: Sie leitet sich von ab Sprite, die entweder den Pfad eines Bildes oder eine bereits geladene Textur in ihrem Konstruktor akzeptiert. TextLädt eine Textur in seinen Konstruktor und übergibt diese an Sprite.

Können Sie ein Design skizzieren, das sinnvoller ist? Oder weisen Sie mich auf eine gute objektorientierte Referenzcodebasis für ein 2D-Spiel hin? Ich konnte nur 3D-Engine-Codebasen mit anständiger Codequalität finden, z. B. Doom 3 und HPL1Engine.

Futlib
quelle

Antworten:

8

Im Allgemeinen ist es keine schlechte Idee, die Spielmechanik von ihrer visuellen Darstellung zu trennen. Wenn Sie eine Spielelogik haben, die einerseits nicht weiß, wie sie angezeigt wird, und eine Grafik-Engine, die eine visuelle Darstellung des aktuellen Spielstatus erstellt, sich aber nicht darum kümmert, wie dieser Status andererseits erstellt wird, ist Ihre Code wird viel flexibler sein. Sie können beispielsweise problemlos eine alternative visuelle Darstellung des Spielstatus erstellen, z. B. eine Minikarte, eine Übersichtskarte oder eine völlig andere Perspektive. Sie können sogar von einer 2D-Engine zu einer 3D-Engine wechseln, ohne den Code der Spielmechanik zu berühren. Ihre visuellen Darstellungen wären ebenso flexibel. Wenn Ihre Grafik-Engine spielunabhängig ist,

Daher würde ich Sprite nicht als Basisklasse empfehlen.

Ein weiterer Grund ist, dass es in Ihrer Spielszene möglicherweise Objekte gibt, die keine visuelle Darstellung haben (z. B. Ereignisauslöserbereiche). Oder Sie könnten Objekte haben, die mehr als ein Sprite haben. In einem 2D-RPG-Projekt, an dem ich teilgenommen habe, hatten wir Charaktere mit einem nackten Basis-Sprite und mehreren Stoff-Sprites, die über das Basis-Sprite gelegt wurden. Also hatte jede Entität 0..n Sprites. Ich könnte mir auch andere Genres vorstellen, in denen es sinnvoll sein könnte, Objekte zu haben, die durch mehrere übereinander gestrichene Bilder dargestellt werden.

Das Erstellen Ihrer Objekthierarchie ist sehr anwendungsspezifisch und hängt stark von Ihrem Gameplay ab. Es ist daher schwierig, eine eindeutige Richtlinie bereitzustellen.

Philipp
quelle
Wow, ich denke, Sie haben mich überzeugt, die Zeichenlogik von der Spiellogik zu trennen, viele gute Punkte. Aber: Sind Dinge wie die Position oder Rotation meiner Entitäten nicht Teil der Spiellogik? Ich muss diese Eigenschaften entweder zwischen Entität und Drawable synchronisieren, um eine bidirektionale Verbindung zu haben. Nichts davon klingt für mich wünschenswert.
Futlib
Die Rotation und die Position in der Spielwelt sind Teil Ihrer Spiellogik. Die Position auf dem Bildschirm ist jedoch nicht (Ihr Bildschirm könnte scrollen und nur einen Teil der Spielwelt anzeigen). Es ist die Aufgabe Ihrer Zeichenlogik, die von der Spiellogik bereitgestellten Weltkoordinaten zu lesen, in Bildschirmkoordinaten zu übersetzen und die visuelle Darstellung an diesen Koordinaten zu zeichnen.
Philipp
Ich wollte diesen Ansatz ausprobieren, stieß aber sofort auf Probleme. Ich habe eine bequeme Methode zum Zentrieren von Objekten auf dem Bildschirm, die ich an einigen Stellen verwende. Es ist nicht mehr sinnvoll, diese Option zu Entityaktivieren, aber die Szene erhält keinen Zugriff auf die Drawable. Steuert auch Entityweiterhin, wie es angezeigt wird, da es ein geeignetes auswählt Drawable. Es macht einfach nicht mehr die Details. Können Sie mir ein kurzes Beispiel für das System geben, an das Sie denken, oder mich auf eine Codebasis verweisen, die es verwendet?
Futlib
Oder schlagen Sie vor, dass ich die gesamte Szene an meinen Renderer weitergebe und er entscheidet, wie jede einzelne Entität gerendert wird?
Futlib
1
Ja, das würde ich Ihnen empfehlen.
Philipp
3

Im Allgemeinen sollten Sie die Komposition nach Möglichkeit der Vererbung vorziehen. In diesem Beispiel hätte ich Sprite nicht als Basisklasse, von der Spielobjekte abgeleitet sind. Ob ein Sprite gezeichnet werden soll oder nicht, ist nur eine Eigenschaft des Entity-Objekts. Möglicherweise folgen Sprite und Text einer Drawable-Oberfläche und ordnen sie entsprechend den Entitäten zu.

Kylotan
quelle
Sie schlagen also vor, dass ich die Zeichnungslogik aus der Entität entferne und jede Entität eine geeignete Implementierung einer DrawableSchnittstelle nach Komposition verwendet? Wenn dies der Fall ist - wie könnte die Szene die Entitäten sich draw()selbst mitteilen, wenn sie sich nicht in ihrer Benutzeroberfläche befinden? Und sollte ich Komposition nicht nur für "has-a" -Beziehungen bevorzugen? Ich denke, es ist in diesem Fall "is-a".
Futlib
1
@futlib Nein, er bedeutet, dass Sie eine "Komponente" (in diesem Fall RenderComponent) haben, die Sie Ihrer Entität hinzufügen. Ihre Komponenten sind Objekte für sich (und manchmal nur Daten ohne Funktionen), Teile, die Sie in Ihre Entitäten "einstecken" können. Edit: Ich glaube, ich bin ein Kopf von mir gegangen. Vielleicht hat Kylotan gemeint, was Sie gerade gesagt haben.
Sidar
Wenn ich mehr darüber nachdenke, könnte die Beziehung als "has-a" erklärt werden: Die Texteinheit hat einen Text, einen Schriftstil und eine Zeichnung, die ihn auf dem Bildschirm darstellt. Trotzdem muss ich Position, Drehung, Größe und Schriftstil zwischen dem Textelement und seinem Zeichenobjekt synchronisieren, damit es ein bisschen nach "is-a" riecht. Die Frage ist: Wenn ich eine Beziehung möglicherweise mit "has-a" beschreiben kann, könnte ich mich dann für eine Komposition entscheiden? Oder gehen Sie für die natürlichere / überzeugendere Beschreibung der Beziehung?
Futlib
1
+1 für "Komposition der Vererbung vorziehen". Dies ist eine klassische Empfehlung in der OOP-Programmierung.
topright
Ich meinte nicht unbedingt eine explizite 'Komponente', sondern nur die visuelle Darstellung als separates Objekt zur logischen Darstellung. Die visuelle Darstellung kann ein Sprite-Objekt sein, das in einem Entity-Objekt enthalten ist oder auf andere Weise verknüpft ist.
Kylotan