Entwicklung eines flexiblen Motors auf Fliesenbasis

8

Ich versuche, eine flexible, auf Kacheln basierende Spiel-Engine zu erstellen, um alle Arten von Nicht-Echtzeit-Puzzlespielen zu erstellen, genau wie Bejeweled, Civilization, Sokoban und so weiter.

Der erste Ansatz, den ich hatte, bestand darin, ein 2D-Array von Kachelobjekten zu haben und dann Klassen von Kacheln zu erben, die die Spielobjekte repräsentierten. Leider konnte ich auf diese Weise nicht mehr Spielelemente auf demselben Plättchen stapeln, ohne ein 3D-Array zu haben.

Dann habe ich etwas anderes gemacht: Ich hatte immer noch das 2D-Array von Kachelobjekten, aber jedes Kachelobjekt enthielt eine Liste, in die ich sie legte, und verschiedene Objekte. Dies hat gut funktioniert, bis ich vor 20 Minuten feststellte, dass es zu teuer ist, viele Dinge zu tun. Schauen Sie sich dieses Beispiel an:

Ich habe eine Wall-Entität. Bei jedem Update muss ich die 8 benachbarten Kacheln überprüfen, dann alle Entitäten in der Kachelliste überprüfen, prüfen, ob eine dieser Entitäten eine Wand ist, und schließlich das richtige Sprite zeichnen. (Dies geschieht, um Wände zu zeichnen, die nahtlos nebeneinander liegen.)

Die einzige Lösung, die ich jetzt sehe, ist ein 3D-Array mit vielen Ebenen, das für jede Situation geeignet ist. Auf diese Weise kann ich jedoch nicht zwei Objekte stapeln, die dieselbe Ebene auf derselben Kachel haben. Wann immer ich das tun möchte, muss ich eine neue Ebene erstellen.

Gibt es eine bessere Lösung? Was würden Sie tun?

Vee
quelle
Wie würde die Verwendung eines 3D-Arrays Ihr Problem mit dieser Wandprüfungssituation beheben? Wäre es nicht immer noch dasselbe?
Michael Coleman
Ich würde wissen, dass Wände nur auf Ebene 1 bleiben. Also kann ich einfach Folgendes tun: Fliesen [Wall.X - 1, Wall.Y, 1] ist Wand?
Vee
Dann können Sie nicht einfach das erste Element in der Liste überprüfen? Ich sehe nicht, wie eine Liste ein Problem aufwirft.
Michael Coleman
Die Wand kann sich mit dem zweiten Ansatz an einer beliebigen Stelle in der Liste befinden. Ich muss jede Entität durchlaufen und prüfen, ob es sich um eine Wand handelt.
Vee
4
Hatten Sie tatsächlich ein Leistungsproblem oder haben Sie nur Angst, dass Sie dies tun würden? Hast du es profiliert?
herrlicher

Antworten:

2

Hast du tatsächlich gesehen dieses Problem oder haben Sie es sich gerade ausgedacht? Weil ich nicht sehe, wie sich das Durchlaufen der Objektliste spürbar auf die Leistung auswirkt. Sie müssten Hunderte von Objekten pro Kachel haben, damit dies eine Rolle spielt. Die meisten Spiele, die ich mir vorstellen kann, haben 10 Tops.

Wenn Sie wirklich ein Problem haben, ist es am besten, Daten zwischenzuspeichern und nicht die Darstellung zu ändern. In Ihrem Beispiel ändert die Wandkonfiguration wahrscheinlich nicht jeden Frame. Cache einfach den richtigen Sprite-Typ zwischen und berechne ihn nicht ständig neu.

Wenn Sie ein verrücktes Spiel mit vielen Objekten erstellen möchten, die sich ständig ändern, können Sie verschiedene Ebenen mit unterschiedlicher Semantik erstellen. Beispielsweise enthält die Wandebene nur Wände, nicht mehr als 1 pro Kachel, und die Dekorationsebene enthält beispielsweise Listen von Objekten, die sich nie ändern.

Wenn Sie eine ultra-flexible Architektur wünschen, die jedes mögliche Spiel ideal unterstützt - Pech -, gibt es so etwas nicht.

Keine Ursache
quelle
2

Zwei Vorschläge. Zunächst sollten Sie das Sprite auflösen, mit dem jede Kachel beim Laden Ihrer Kachelkarte / -ebene gezeichnet wird. Es sollte einfach genug sein, das 2D-Array zu durchlaufen, wenn Ihr Level geladen ist, und zu entscheiden, dass eine Certian-Wand ein L sein muss Form und eine andere eine | haben mussgestalten.

Zweitens würde ich statische Daten, die sich auf eine Kachel beziehen, an einem anderen Ort als aktive Daten speichern. Ein Kachelobjekt kann also eine Liste von Objekten haben, die von der Kachel kommen und gehen, aber es besteht keine Notwendigkeit, diese zu durchlaufen, um zu sehen, ob z. B. wir nur wissen möchten, ob die Kachel begehbar ist oder nicht.

Dieser Psudo-Code ist mehr oder weniger so, wie ich mich einem kachelbasierten System genähert habe.

Tile {
  Texture tex;
  //...other static data...
  List currentObjects;
}

Tile t = Tiles[x][y];

Dies setzt voraus, dass es in Ihrem Spiel statische Kacheln gibt, aber das ist eine ziemlich gute Annahme in vielen kachelbasierten Spielen, in denen Sie mit Wänden und dergleichen zu tun haben.

Nick Van Brunt
quelle
1

Du sagtest:

Ich musste ein 2D-Array von Tile-Objekten haben und dann Klassen von Tile erben, die die Spielobjekte repräsentierten. Leider konnte ich auf diese Weise nicht mehr Spielelemente auf demselben Plättchen stapeln, ohne ein 3D-Array zu haben.

Dann habe ich etwas anderes gemacht: Ich hatte immer noch das 2D-Array von Kachelobjekten, aber jedes Kachelobjekt enthielt eine Liste, in die ich sie legte, und verschiedene Objekte. Dies funktionierte gut bis vor 20 Minuten, als mir klar wurde, dass es zu teuer ist, viele Dinge zu tun

Dies ist genau die Situation, die Sie mithilfe des MVC-Musters (Model View Controller) lösen möchten, um Ihr Modell von Ihrer Ansicht zu trennen. MVC macht dieses Problem aus zwei Teilen:

  • So modellieren Sie einen Stapel Kacheln
  • Wie zeige ich einen Stapel Kacheln an?

Anstatt ein 2D-Array von Kacheln oder ein 2D-Array einer Liste von Kacheln zu haben, würde Ihr Modell ein 2D-Array von Spielobjekten speichern. Dann betrachtet Ihr 2D-Array von Kachelklassen die entsprechenden Spielobjekte und entscheidet, wie sie gerendert werden sollen.

Anstatt dass eine einzelne Tile-Instanz ein einzelnes Spielobjekt rendert, können Sie eine einzelne Tile-Instanz etwas rendern lassen, das wie ein Stapel von Objekten aussieht . Dies wäre effizienter und würde Ihnen eine saubere Trennung Ihrer Kerncodelogik und ihres Aussehens ermöglichen.

Genau wie Wände. Die Logik ist sehr trivial, aber das Rendern ist komplexer.

Asche999
quelle
3
Ich sehe nicht, wie MVC (was meiner Erfahrung nach in Spielen nicht üblich ist) hier helfen würde. Wenn überhaupt, würde es es noch schlimmer machen: Es würde zu einer gewissen Redundanz zwischen dem Modell und der Ansicht kommen, die synchron gehalten werden muss.
herrlicher
1
Ja, wenn Sie ein Problem haben, verwenden Sie einfach <Buzzword>. Jetzt haben Sie zwei Probleme.
Nevermind
1
np, ich habe mehr Details herausgespült.
Asche999
OK, ich habe meine Ablehnung zurückgenommen. Stil, ich denke nicht, dass MVC hier nützlich ist. Meistens unterscheiden sich Objekte durch Logik, nicht (nur) durch grafische Darstellung.
Nevermind
Kann wohl nicht allen gefallen, denke ich. Für mich ist MVC sofort als Teil der Lösung eines Teils des Problems herausgesprungen. Aber ich stimme zu, dass ich nicht alle Probleme vollständig lösen kann - das würde einen Aufsatz erfordern!
Asche999
0

Ich stimme @Omnion zu. Verwenden Sie den Listenansatz, aber sortieren Sie ihn, wenn die Leistung ein Problem darstellt. Verwenden Sie also einen hybriden Ansatz. Verwenden Sie die Liste als dritte Dimension, identifizieren Sie jedoch nur die ersten 5 Elemente als einen bestimmten Typ. Danach handelt es sich um einen tinteneigenen Typ oder um einen Typ, der nicht mehrmals pro Frame überprüft werden muss.

Nate
quelle
0

Sie sollten "nie" separate Klassen für Dinge wie Kacheln erstellen. Sie sollten eine Struktur aus Bytes, Shorts und Ints erstellen, die sich auf den Typ der Kachel und der darauf befindlichen Objekte beziehen. Leistungsbezogene Ints sind am besten für 64-Bit-Systeme geeignet oder sogar lang. Wenn Sie Ihre Karte jedoch jemals speichern möchten, sollten Sie Bytes oder Shorts verwenden, wo immer Sie können, insbesondere bei großen Karten.

In meinen Spielen habe ich eine Kartenklasse, die nur Felder enthält wie: byte tileType; 256 verschiedene Geländetypen reichen für dieses Spiel aus. Nimmt 1 Byte. ushort tileObject; 65.536 verschiedene Objekte sind ausreichend. Nimmt 2 Bytes usw.

Wenn ich das obige Beispiel nehme, benötigt jede Kachel nur 3 Bytes im Speicher. Ein 10000x10000 wäre also 300 MB im Speicher und sollte kein Problem sein. Wann immer Sie nach etwas suchen müssen, bestimmen Sie, wo auf der Karte Sie suchen möchten und wonach Sie suchen, und iterieren die Arrays entsprechend.

Wenn Sie überall auf der Karte dynamische Dinge haben, fragen Sie sich, ob der Spieler diese Dinge wirklich weit außerhalb seines Ansichtsfensters bemerkt.

Madmenyo
quelle