Gemeinsames Muster zum Skalieren von „realen Einheiten“ auf Pixel?

10

Dies ist eine Folgefrage zu dieser anderen .

Ich würde gerne wissen, ob es ein gemeinsames / typisches / bestes Muster gibt, um meine Darstellung der Welt (derzeit 160 km x 160 km) so zu skalieren , dass sie in den Zeichenbereich (derzeit 800 x 600 Pixel) passt .

Ich kann mir mindestens vier verschiedene Ansätze vorstellen:

Naiv (so wie ich es bisher gemacht habe). Ich habe eine globale Funktion implementiert sc(vector), die einfach eine verkleinerte Kopie des übergebenen Vektors zurückgibt. Dies funktioniert natürlich, verpflichtet mich jedoch, Code wie folgt zu schreiben:

drawCircle(sc(radius), sc(position))

Verpackungsfunktionen . Ich könnte mehrere Funktionen definieren, von denen jede die ursprüngliche Middleware umschließt. Zum Beispiel könnte ich definieren, myDrawCircledass zuerst die Argumente skaliert werden, die skaliert werden müssen, und dann drawCirclemit letzterem aufgerufen werden . Dies würde meinen Code vielleicht lesbarer und leichter zu pflegen machen, aber ich sollte viele Wrapping-Funktionen schreiben, die albern klingen.

Funktionsdekorateur . Ich könnte einfach die vorhandenen Middleware-Funktionen dekorieren und eine automatische Skalierung für alle Parameter bereitstellen, die eine Instanziierung der Klasse darstellen Vector3D. Das Problem ist jedoch, dass diese Funktionen auch mit denselben Parametern funktionieren listoder Vector2Dauch, und der Dekorateur hätte keine Möglichkeit, dies zu wissen Welche Listen müssen skaliert werden (z. B. der Radius) und welche nicht (die RGB-Werte).

Oberflächeninitialisierung . Bei der Definition der Oberfläche, auf die ich zeichnen werde, könnte ich den Skalierungsfaktor definieren (so dass ich dann Meter und nicht Pixel als Parameter verwenden würde). Dies würde für mich transparent funktionieren und wäre meine bevorzugte Lösung, aber natürlich sollte es in der Middleware implementiert werden, daher ist es keine echte Option.

... jedenfalls: Da dies ein sehr häufiges Problem ist, frage ich mich, ob es ein etabliertes Muster gibt, das dieses Problem, das ich nicht gefunden habe, elegant löst.

PS: Für dieses Projekt verwende ich Python (mit Pygame ), aber - obwohl eine Python / Pygame-spezifische Antwort sehr geschätzt wird, interessiert mich eher die allgemeine / allgemeine Beschreibung des Musters als dessen konkrete Implementierung.

Vielen Dank im Voraus für Ihre Zeit und Ihr Fachwissen.

Mac
quelle

Antworten:

6

Die Standardmethode hierfür ist das Einrichten einer Transformationsmatrix für die Konvertierung während des Renderns. Beim 3D-Rendering ist es die Ansichtstransformationsmatrix für die Kamera, die dies tut.

Die meisten 2D-APIs bieten auch die Möglichkeit, eine Ansichtstransformation entweder als 2x3-Matrix oder als separate Skalierung, Translation und Rotation anzugeben.

Ein kurzer Blick auf die Pygame-Dokumentation legt nahe, dass Sie selbst eine Ansichtstransformationsmatrix implementieren müssen. Können Sie damit Ihre eigene Klasse aus der Oberflächenklasse ableiten, um diese Funktionalität hinzuzufügen?

Adam
quelle
Ihre Antwort ist sehr nützlich (+1). Ich kann meine Oberflächenklasse unterordnen, aber ich brauche mehr Zeit, um herauszufinden, ob ich das erreichen kann, was in Ihrem Beitrag angegeben ist. Ich bin sehr neu in der Spielprogrammierung und Grafik im Allgemeinen. Haben Sie einen Link, der diesen Matrixansatz erklärt (oder sogar nur die Mathematik, bei der eine Matrix zum Skalieren einer Oberfläche verwendet wird?). Vielen Dank!
Mac
1
Da es sich um 2D handelt und keine Rotation erforderlich ist, sagt @mac im Grunde, dass Funktionen umbrochen werden sollen, und Sie können Ihre einfache Skalenteilung in diesen Wrappern verwenden. Der Grund, warum normalerweise eine vollständige Matrix verwendet wird, ist, dass die meisten APIs eine Matrix haben, die das Ansichtsfenster steuert. Es scheint jedoch, dass Pygame dies nicht tut, sodass Sie es eine Ebene höher selbst tun müssen.
Patrick Hughes
2

Da dies ein sehr häufiges Problem ist, frage ich mich, ob es ein etabliertes Muster gibt, das dieses Problem, das ich nicht gefunden habe, elegant löst.

Normalerweise versuchen Benutzer nicht, eine Laufzeitskalierung in 2D-Spielen durchzuführen. Ich stimme nicht zu, dass es ein häufiges Problem ist. Wenn Benutzer eine Minikarte oder eine andere feste Neuskalierung wünschen, werden häufig neue Grafiken für diesen Zweck erstellt. Es ist selten, dass Sie eine einzelne Routine benötigen, um in einem 2D-Spiel mit 2 verschiedenen Zoomstufen zu arbeiten.

Wenn Sie eine 3D-API verwenden, können Sie diese Funktionalität kostenlos nutzen - ändern Sie einfach die Kamera- / Ansichtsfenster-Parameter.

PyGame ist eine sehr alte API und leider nur für 2D geeignet. Selbst wenn Sie geeignete Skalierungssysteme für die verschiedenen Zoomstufen ausarbeiten könnten, ist die Leistung nicht gut genug und das Erscheinungsbild ist wahrscheinlich inakzeptabel schlecht.

Ich würde empfehlen, dass Sie, wenn Sie viel vergrößern und verkleinern möchten, so schnell wie möglich auf eine moderne 3D-API umsteigen. Ich würde Pyglet empfehlen, aber es gibt wahrscheinlich auch andere.

Kylotan
quelle
Vielen Dank für Ihre Antwort (+1). Ich habe bereits pygletsin der Vergangenheit für eine 3D-Simulation verwendet. Es ist sicherlich leistungsfähiger als pygame, aber die Unterstützung für 2D ist ziemlich niedrig als die in pygame. Die verfügbaren Dokumentationen / Beispiele sind auch weniger detailliert, was für mich als Anfänger in der Spieleentwicklung ein Nachteil ist und ich die Grundlagen der am häufigsten durchgeführten Operationen wirklich verstehen muss. :) Was das "Alter" von Pygame betrifft: Du hast recht. Eine Modernisierung ist jedoch im Gange! :)
Mac
Was: "Es ist selten, dass Sie eine einzelne Routine benötigen, um mit 2 verschiedenen Zoomstufen zu arbeiten" ... Ich dachte, das würde verwendet werden, wenn Sie die Größe eines Fensters / die Auflösung eines Vollbildspiels ändern. Aus Ihrem Kommentar habe ich verstanden, dass es nicht so ist: irgendein Zeiger auf das, was stattdessen "gewöhnlich" ist?
Mac
Welche 2D-Operationen führen Sie aus, bei denen Pyglet niedriger ist als Pygame? Mit beiden können Sie ein Sprite mit 1 Codezeile zeichnen. Wenn Sie die Fenstergröße eines 2D-Spiels ändern, bleibt das Verhältnis von Pixel zu Welteinheit fast immer konstant. Am Ende zeichnest du einfach mehr Sprites: du zeichnest sie nicht anders.
Kylotan
Auf der niedrigen Seite: Ich bin kein wirklicher Experte für eine der beiden Bibliotheken, aber in Pyglets konnte ich das von Pygame angebotene Gruppen-Rendering-Verhalten (wie pygame.sprite.LayeredUpdateszum Beispiel) nicht sofort replizieren . Was das unveränderliche Verhältnis betrifft: Ich habe verstanden, was du meinst. Das Verhalten, das Sie beschreiben, möchte ich nicht wiederholen, aber ich habe verstanden, was Sie meinen, danke für die Klarstellung! :)
Mac
pyglet hat OrderedGroup-Objekte - Dokumente sind hier, wenn Sie oder jemand anderes interessiert ist: pyglet.org/doc/programming_guide/displaying_images.html#sprites
Kylotan