Isometrie, Z-Indizes in Handyspielen und deren Optimierung


Hallo Habr! Kürzlich haben wir unser Spiel herausgebracht , das wir lange vorbereitet haben und in dem sich eine beträchtliche Anzahl interessanter Themen angesammelt hat, die es wert sind, mit der Community geteilt zu werden. Das Thema wird nicht nur für iOS und andere mobile Entwickler interessant sein, sondern auch für alle, die daran interessiert sind, wie alle möglichen grafischen Dinge unter der Haube funktionieren, sowie für alle Fans von 2D-Strategien, die ich selbst seit dem dritten Jahrzehnt bin.

Heute werden wir über die Nuancen eines so wichtigen Themas wie Z-Indizes auf einer isometrischen Oberfläche sprechen (ja, nicht alles ist so einfach, wie es einigen Weisheiten erscheint). In der 3D-Welt haben wir seltsamerweise drei Koordinaten - x, y, z -, die die Position des Objekts im Raum vollständig bestimmen. Die Aufgabe, die Nähe von Objekten zur Kamera zu bestimmen, liegt ebenfalls dort, liegt jedoch vollständig auf den Schultern von OpenGL. Der Entwickler arbeitet nur mit allgemeinen Parametern wie der Z-Puffer-Tiefe, die sich auf die Leistung auswirken. Andernfalls können Sie OpenGL als Black Box vertrauen - es verfügt über genügend Informationen.

Eine völlig andere Situation wird in unserer "Pseudo-3D" -Welt beobachtet - jedes Objekt hat nur (x, y) - Koordinaten und Größe des Sprites. Die erste Aufgabe, der sich ein Programmierer beim Schreiben einer Engine gegenübersieht, besteht darin, zu bestimmen, welche Objekte sich vor unserer virtuellen „Kamera“ überlappen sollen.

Synopsis


SpriteKit-Koordinaten (wobei (0; 0) das Zentrum der „Welt“ ist und Y steigt) sind in diesem Fall überhaupt nicht interessiert, weil Sie bedeuten nichts in unserer isometrischen „Welt“ mit Ihnen. Machen wir also eine Reservierung - wir haben ein rautenförmiges Feld wie Age of Empires.



Eine Kachel mit Koordinaten (0; 0) befindet sich in der linken Ecke der Raute, die Abszisse X nimmt "unten" und "rechts" zu, d.h. wächst näher an den Beobachter heran, die Ordinate Y nimmt "nach oben" und "nach rechts" zu, d.h. nimmt ab, wenn Sie sich dem Beobachter nähern.

Außerdem sollten sich die Schienen „unter“ dem Zug befinden, der Rauch aus dem Schornstein sollte sich „über“ dem Zug befinden. Aber wir werden uns jetzt nicht mit den „Schichten des Seins“ beschäftigen - offensichtlich hindert uns nichts daran, so viele isometrische „Scheiben“ zu machen, wie Sie möchten, und nach denselben Regeln zu arbeiten. Wir gehen davon aus, dass es in einer Kachel immer ein Objekt gibt - aus Gründen der Klarheit ist mehr nicht erforderlich.



Betrachten Sie die beiden Züge oben. Aus Sicht des Beobachters sollten sich die Wagen offensichtlich "unter" dem Zug befinden, d. H. ihr Z-Index sollte kleiner sein. Gleichzeitig sollte sich der „obere“ Zug mit dem Nachbarn „überlappen“, „weiter“ sein. Können wir mit nur den Koordinaten (x; y) eine Karte der Z-Indizes für jede Kachel erstellen?

Natürlich ja, unter Verwendung der folgenden Formel (Pseudocode a la swift):

zIndex = pos.x * field.size.width - pos.y 

Somit garantieren wir, dass sich die Objekte mit zunehmender Ordinate entfernen (-pos.y) sowie mit dem Wachstum der Abszisse die Objekte nähern (pos.x) und, was wichtig ist, jedes Objekt, das eine Abszisse hat, beispielsweise 44, absichtlich „näher“ ist. "Als jedes Objekt mit einer Abszisse 43. Um hier" Schichtung "hinzuzufügen (denken Sie daran, die Schienen unter dem Zug, Rauch über dem Schornstein), reicht es aus, eine konstante" Höhe "der Schicht hinzuzufügen:

 zIndex = layerZIndex + pos.x * field.size.width - pos.y 

Das war's, Sie können den Artikel beenden und sich für die Grundlagen der Stereometrie loben, die Sie in der 10. Klasse gelernt haben, und die Spielelogik starten. Nein? Wenn! Ich würde über die offensichtlichen Dinge schreiben! (Nun, wie offensichtlich, sind ein paar Tage zerquetscht und dies)

Wir kommen gerade zum lustigen Teil und gehen weiter.

Leistungskampf


Jeder hat mindestens einmal ein Testprojekt für SpriteKit (oder eine Kokosnuss oder eine andere Engine) durchgeführt und dabei magische Zahlen gesehen - fps und Knoten.



Offensichtlich ist fps die Anzahl der Frames pro Sekunde, Knoten die Anzahl der Knoten, hauptsächlich Sprites. In der Praxis wird fps jedoch vor allem nicht durch die Anzahl der Knoten festgelegt, sondern durch einen anderen Parameter, der standardmäßig nicht angezeigt wird, der aber auch mit einer Zeile angezeigt werden kann - die Anzahl der neu gezeichneten Ziehungen.



Wie Sie jetzt sehen, beträgt die Anzahl der Knoten in derselben Szene ungefähr 6000 und die Anzahl der Renderings ungefähr 120. Dies ist der minimale Zoom (die Kamera befindet sich so nah wie möglich an der Oberfläche), 1: 1.

Bewegen wir nun die Kamera auf die maximale Entfernung (in unserem Spiel ist es 2,5: 1).



Wir haben die Skalierung nur um das 2,5-fache geändert (im Beispiel werden weit davon entfernt, dass alle Objekte gezeichnet werden), und die Anzahl der Ziehungen wurde bei unveränderter Knotenanzahl um das 5- bis 6-fache erhöht!

Natürlich beeinflusst die Anzahl der Renderings fps nicht wesentlich mehr als die abstrakte Anzahl der Knoten. SpriteKit zeichnet einfach keine Knoten, die nicht in das Ansichtsfenster (in die Kamera) fallen. Die einzige Ausnahme, die ich bisher gefunden habe, sind Partikelemitter, die immer gezeichnet werden, unabhängig davon, ob sie sichtbar sind oder nicht.

Lassen Sie uns nun darüber sprechen, was dieses "Zeichnen" bedeutet. Die Grafikkarte hat alle Knoten "Ebenen", die von ihren Z-Indizes geleitet werden. Und es geht immer wieder durch das ganze Bild, vom niedrigsten zum höchsten. Die Anzahl solcher Renderzyklen wird gezogen.

Jetzt verstehen Sie, dass wenn Sie jedes winzige Objekt (und wir haben eine große Karte, ungefähr 6000 x 3000) mit einem eigenen Z-Index zeichnen, dies die Leistung eines Telefons beeinträchtigt.

Probleme treten am besten bei den alten 5 und 5 auf, aber das Vorhandensein des iPhone 10 garantiert nichts - mit dem falschen Ansatz können Sie jede leistungsstarke Hardware loswerden. In unserem Spiel war einer der Eckpfeiler extreme Klarheit. Beim nächsten Zoom entsprechen Eins-zu-Eins-Sprites Retin-Pixeln. Ich muss sagen, dass in den meisten Handyspielen die Auflösung um Größenordnungen niedriger ist, daher sind die Anforderungen nicht so enorm, aber wir haben es qualitativ gemacht, wie für uns selbst ...

Also musst du zu den Tricks gehen.

  • Alle Objekte, mit denen der Spieler nicht interagiert und die sich in der X-Koordinate auf derselben Ebene befinden, können im Allgemeinen zu einem Sprite zusammengeführt werden. Für eine Grafikkarte ist es viel einfacher, ein großes Sprite als 10 kleine zu zeichnen. Daher sind Waldwege zwischen Straßen integrale Sprites, die aus mehreren Bäumen bestehen. Und Bäume, die Pfade und andere Bäume nicht überlappen, werden im Allgemeinen in die Karte eingenäht. In Alpha gab es übrigens einige Fehler, als die jahrhundertealte Eiche direkt unter den Schienen eines Zuges oder unter einer Ampel wuchs. Testen Sie Ihr Spiel also sorgfältig, um die Benutzer nicht zu amüsieren.
  • Objekte mit einem Z-Index werden in der Reihenfolge gezeichnet, in der sie auf der Grafikkarte angezeigt werden. Das heißt, Wenn Sie "entfernte" Objekte vor "nahen" Objekten hinzufügen, fallen diese korrekt, erhöhen jedoch nicht die Anzahl der Grafikkartenrender.

All dies ermöglicht es Ihnen, die Anzahl der Ziehungen zeitweise zu reduzieren und fps sogar auf einem alten iPhone zu korrigieren. Ich musste einige Effekte stark auf sie beschränken, aber Apple hat seit einem Jahr keine Updates für sie veröffentlicht - eine Sünde wird sich beschweren!

Höhe


Nun, alles, der Motor ist fertig, kannst du schon etwas Interessantes starten? Jemand kann, aber es ist zu früh für uns. Schließlich sollte der Zug den Tunnel schön verlassen, und hier ist nicht alles so einfach, wie es scheint.



Der Zug sollte „höher“ als die „entfernte“ Wand des Tunnels und „niedriger“ als das Dach des Tunnels und die darauf folgenden Berge liegen. Es ist schließlich schön, wenn die Karte so vielschichtig ist und Höhenunterschiede aufweist. Auch hier machen wir keinen seelenlosen Unsinn, sondern das, was wir selbst mögen!

Aber zurück zu den Details - dafür wurde die Karte wie folgt „geschnitten“.



Die Innenwand des Tunnels und alles andere links ist niedriger und



die Spitze des Tunnels zusammen mit den Bergen, in die er fließt. Hier hilft keine prozedurale Generierung von Z-Indizes, nur ein strenger belarussischer Hardcode.

Der aufmerksame Habrayuzer bemerkte im Screenshot des Spiels, dass in der Nähe der Tunnel die Bäume ordentlich „gemäht“ wurden, wodurch der unberührte Strandsand freigelegt wurde. Dieser scheinbare Fehler ergibt sich aus der grundsätzlichen Unmöglichkeit, solche Baumpflanzungen in 2D zu realisieren. Der Zug, der den Tunnel verlässt, muss sich bewusst „über“ den überlappenden Bäumen befinden und diese mit sich selbst bedecken. Aber diese gleichen Bäume sollten das Dach des Tunnels überlappen, unter dem der Zug einfahren sollte! Und das Dach sollte höher sein als der Zug, und so haben wir im Kreis einen logischen Widerspruch ...

Aus einem ähnlichen Grund gibt es in alten Spielen wie Duke Nukem und Doom2 aufgrund der Unvollkommenheit der Grafik-Engine keine großen Unterschiede in der Höhe und dem Boden von Gebäuden.

Deshalb wachsen keine Bäume in der Nähe der Tunnel.

Ich hoffe es war interessant, das Spielzeug live hier (kostenlos zu spielen) , der nächste Artikel in der Serie wird sich mit wunderschönem realistischem 2D Wasser befassen, verpassen Sie es nicht!

PS Übrigens kann ein Video, das Aufmerksamkeit erregt, auf Youtube in normaler Qualität angesehen werden.

PPS Das Spiel ist bisher nur in den GUS-Staaten, Kanada und Irland verfügbar. Wenn jemand aus anderen Ländern schauen möchte, sende eine persönliche E-Mail mit appleId - ich werde es TestFlight hinzufügen

Source: https://habr.com/ru/post/de415051/


All Articles