Entdecke das Potenzial von WebGL in der unendlichen, prozedural generierten Landschaft dieses lässigen Fahrspiels.
Slow Roads ist ein lässiges Fahrspiel, bei dem der Schwerpunkt auf unendlich prozedural generierten Landschaften liegt, die alle als WebGL-Anwendung im Browser gehostet werden. Für viele mag eine so intensive Erfahrung im begrenzten Kontext des Browsers fehl am Platz erscheinen – und tatsächlich war eines meiner Ziele bei diesem Projekt, diese Einstellung auszuräumen. In diesem Artikel gehe ich auf einige Techniken ein, mit denen ich das oft übersehene 3D-Potenzial im Web aufgedeckt habe, um diese Hürde zu überwinden.
3D-Entwicklung im Browser
Nach der Veröffentlichung von Slow Roads sah ich im Feedback immer wieder einen Kommentar: „Ich wusste nicht, dass das im Browser möglich ist.“ Wenn Sie diese Meinung teilen, sind Sie sicherlich keine Minderheit. Laut einer Umfrage vom 2022 State of JS müssen etwa 80% der Entwickler noch nicht mit WebGL experimentieren. Ich finde es schade, dass so viel Potenzial verpasst wird, insbesondere wenn es um browserbasiertes Gaming geht. Mit Slow Roads möchte ich WebGL noch stärker ins Rampenlicht rücken und vielleicht die Anzahl der Entwickler verringern, die sich gegen den Begriff „Hochleistungs-JavaScript-Spiele-Engine“ scheuen.
WebGL mag für viele mysteriös und komplex erscheinen, aber in den letzten Jahren haben sich seine Entwicklungsökosysteme zu leistungsstarken und praktischen Tools und Bibliotheken entwickelt. Für Frontend-Entwickler ist es jetzt einfacher denn je, 3D-UX in ihre Arbeit einzubinden, selbst ohne Vorkenntnisse im Bereich Computergrafik. Three.js, die führende WebGL-Bibliothek, dient als Grundlage für viele Erweiterungen. Dazu gehört auch react-three-fiber, das 3D-Komponenten in das React-Framework einbindet. Es gibt jetzt auch umfassende webbasierte Spieleeditoren wie Babylon.js oder PlayCanvas, die eine vertraute Benutzeroberfläche und integrierte Toolchains bieten.
Trotz des erstaunlichen Nutzens dieser Bibliotheken sind anspruchsvolle Projekte jedoch letztendlich an technische Grenzen gebunden. Bedenken bezüglich des Konzepts des browserbasierten Spielens könnten hervorheben, dass JavaScript Singlethread und Ressourcenbegrenzt ist. Doch durch die Nutzung dieser Einschränkungen eröffnen sich verborgene Vorteile: Keine andere Plattform bietet die gleiche sofortige Zugänglichkeit und Massenkompatibilität wie beim Browser. Nutzer auf einem beliebigen browserfähigen System können mit nur einem Klick losspielen, ohne Anwendungen installieren oder sich bei Diensten anmelden zu müssen. Außerdem schätzen Entwickler den eleganten Komfort, robuste Frontend-Frameworks für die Erstellung von UI-Elementen oder die Netzwerkverwaltung für Mehrspielermodi bereitzustellen. Diese Werte sind es, die den Browser zu einer so hervorragenden Plattform für Spieler und Entwickler machen – und wie von Slow Roads gezeigt, lassen sich die technischen Einschränkungen häufig auf ein Designproblem reduzieren.
Reibungslose Leistung bei langsamen Straßen
Die Kernelemente von Slow Roads sind schnelle Bewegungen und die Generierung teurer Landschaften, daher unterstreicht die Notwendigkeit einer reibungslosen Leistung meine Designentscheidung. Meine Hauptstrategie bestand darin, mit einem einfachen Spieldesign zu beginnen, das kontextabhängige Verknüpfungen in der Architektur der Engine ermöglichte. Der Nachteil: Sie müssen einige Zusatzfunktionen in Anspruch nehmen, um Minimalismus zu vermeiden. Das Ergebnis ist dann aber ein individuelles, hochoptimiertes System, das mit verschiedenen Browsern und Geräten gut funktioniert.
Im Folgenden finden Sie eine Aufschlüsselung der wichtigsten Komponenten, die für eine schlanke Straße durch langsame Straßen sorgen.
Die Umgebungs-Engine auf das Gameplay abstimmen
Als Hauptkomponente des Spiels ist die Engine zur Umgebungsgenerierung unvermeidlich teuer. Es wird angemessenerweise den größten Anteil der Budgets für Arbeitsspeicher und Rechenleistung in Anspruch genommen. Der Trick besteht darin, die umfangreichen Berechnungen über einen bestimmten Zeitraum zu planen und zu verteilen, damit die Framerate nicht durch Leistungsspitzen unterbrochen wird.
Die Umgebung besteht aus geometrischen Kacheln, die sich in Größe und Auflösung unterscheiden, je nachdem, wie nah sie der Kamera angezeigt werden. Sie werden als „Detailebenen“ oder LoDs kategorisiert. In typischen Spielen mit einer kostenlos laufenden Kamera müssen verschiedene LODs ständig geladen und entladen werden, um die Umgebung des Spielers zu erfassen, egal wo er sich gerade befindet. Dieser Vorgang kann kostspielig und verschwenderisch sein, insbesondere wenn die Umgebung selbst dynamisch erzeugt wird. Glücklicherweise kann diese Konvention bei langsamen Straßen aufgrund der kontextabhängigen Erwartung, dass der Nutzer auf der Straße bleiben sollte, vollständig umgangen werden. Stattdessen kann eine detailreiche Geometrie für den schmalen Korridor reserviert werden, der direkt an der Route liegt.
Die Mittellinie der Straße wird weit vor der Ankunft des Spielers generiert. So kann genau vorhergesagt werden, wann und wo die Umgebungsdetails benötigt werden. Das Ergebnis ist ein schlankes System, das proaktiv teure Arbeit planen kann, wobei zu jedem Zeitpunkt nur das Minimum generiert wird, das zu jedem Zeitpunkt benötigt wird, und ohne unnötigen Aufwand für Details, die nicht sichtbar sind. Diese Technik ist nur möglich, weil der Weg ein einzelner Weg ohne Verzweigungen ist – ein gutes Beispiel für Kompromisse beim Gameplay, die architektonische Abkürzungen berücksichtigen.
Wählerisch mit den Gesetzen der Physik
Nach zweiter Rechenleistung der Umgebungs-Engine ist die physikalische Simulation. Bei Slow Roads kommt eine eigens entwickelte, minimale Physik-Engine zum Einsatz.
Die größte Ersparnis besteht hier darin, von vornherein zu vermeiden, zu viele Objekte zu simulieren und sich auf den minimalen Kontext zu konzentrieren, indem Dinge wie dynamische Kollisionen und zerstörbare Objekte reduziert werden. Die Annahme, dass das Fahrzeug auf der Straße bleibt, bedeutet, dass Kollisionen mit Geländeobjekten vernünftigerweise ignoriert werden können. Darüber hinaus ermöglicht die Codierung der Straße als dünnbesetzte Mittellinie elegante Tricks für eine schnelle Kollisionserkennung mit der Fahrbahnoberfläche und Leitplanken, die alle auf einer Entfernungsmessung zum Straßenmittelpunkt basieren. Das Fahren im Gelände wird dann teurer, aber dies ist ein weiteres Beispiel für einen fairen Kompromiss, der für das Gameplay geeignet ist.
Arbeitsspeicher-Fußabdruck verwalten
Als weitere Browser-ressourcenschonende Ressource ist es wichtig, den Arbeitsspeicher mit Sorgfalt zu verwalten – auch wenn JavaScript für die automatische Speicherbereinigung verwendet wird. Das kann leicht übersehen werden, aber selbst die Deklaration von selbst geringer Menge an neuem Arbeitsspeicher in einer Spielschleife kann bei einer Ausführung mit 60 Hz zu erheblichen Problemen führen. Große automatische Speicherbereinigungen können nicht nur die Ressourcen des Nutzers in einem Kontext aufbrauchen, in dem er wahrscheinlich Multitasking ausführt, sondern auch mehrere Frames in Anspruch nehmen, was zu deutlichen Rucklern führen kann. Um dies zu vermeiden, kann Schleifenspeicher bei der Initialisierung in Klassenvariablen vorab zugewiesen und in jedem Frame wiederverwendet werden.
Es ist auch sehr wichtig, dass schwerere Datenstrukturen, wie Geometrien und die zugehörigen Datenpuffer, ökonomisch verwaltet werden. In einem unendlich generierten Spiel wie Slow Roads besteht der Großteil der Geometrie auf einer Art Laufband. Sobald ein altes Stück in der Ferne zurückfällt, können seine Datenstrukturen gespeichert und für ein künftiges Stück der Welt wiederverwendet werden, ein Designmuster, das als Objekt-Pooling bezeichnet wird.
Diese Methoden helfen dabei, eine schlanke Ausführung zu priorisieren, auf die Sie auf eine einfache Programmierfreundlichkeit verzichten müssen. In Hochleistungskontexten ist es wichtig, darauf zu achten, wie Komfortfunktionen manchmal vom Client zum Vorteil des Entwicklers übernommen werden. Methoden wie Object.keys()
oder Array.map()
sind beispielsweise unglaublich praktisch, aber es ist leicht übersehen, dass jede Methode ein neues Array für ihren Rückgabewert erstellt. Wenn Sie die Funktionsweise solcher Blackboxes verstehen, können Sie Ihren Code straffen und irreführende Performance-Treffer vermeiden.
Verkürzen Sie die Ladezeit mit prozedural generierten Assets.
Auch wenn die Laufzeitleistung für Spieleentwickler ein vorrangiges Ziel sein sollte, sind die üblichen Axiome in Bezug auf die anfängliche Ladezeit von Webseiten weiterhin zutreffend. Nutzer sind möglicherweise nachsichtiger, wenn sie wissentlich auf umfangreiche Inhalte zugreifen. Lange Ladezeiten können jedoch die Nutzerfreundlichkeit beeinträchtigen. Spiele erfordern oft große Assets in Form von Texturen, Tönen und 3D-Modellen. Diese sollten zumindest sorgfältig komprimiert werden, wo Details übrig bleiben.
Alternativ können durch die verfahrenstechnische Generierung von Assets auf dem Client langwierige Übertragungen von vornherein vermieden werden. Dies ist ein großer Vorteil für Nutzer mit langsamen Verbindungen und gibt dem Entwickler mehr direkte Kontrolle darüber, wie das Spiel aufgebaut ist – nicht nur für den ersten Ladeschritt, sondern auch, wenn es darum geht, Detailebenen für verschiedene Qualitätseinstellungen anzupassen.
Der größte Teil der Geometrie in Slow Roads wird prozedural generiert und vereinfacht, wobei benutzerdefinierte Shader mehrere Texturen kombinieren, um das Detail zu unterstreichen. Der Nachteil dieser Texturen ist, dass es sich bei diesen Texturen um schwere Ressourcen handeln kann. Dabei gibt es jedoch weitere Einsparmöglichkeiten, da mit Methoden wie stochastischer Textur aus kleinen Quelltexturen mehr Details erzielt werden können. Es ist auch möglich, Texturen auf extremer Ebene mit Tools wie texgen.js vollständig auf dem Client zu generieren. Dasselbe gilt für Audio: Die Web Audio API ermöglicht eine Tongenerierung mit Audioknoten.
Die Erstellung der anfänglichen Umgebung dauert durchschnittlich nur 3, 2 Sekunden. Um die geringe Downloadgröße vorab zu nutzen, begrüßt ein einfacher Ladebildschirm neue Besucher und verschiebt die teure Szene-Initialisierung, bis sie aktiv ist. Dies dient auch als praktischer Puffer für Sitzungen mit Absprüngen und minimiert die Verschwendung der Übertragung von dynamisch geladenen Assets.
Ein agiler Ansatz für eine späte Optimierung
Ich habe die Codebasis für Slow Roads immer als experimentell angesehen und daher einen äußerst agilen Entwicklungsansatz gewählt. Bei der Arbeit mit einer komplexen und sich schnell entwickelnden Systemarchitektur kann es schwierig sein, vorherzusagen, wo die wichtigen Engpässe auftreten. Der Schwerpunkt sollte dabei darauf liegen, die gewünschten Funktionen schnell und nicht sauber zu implementieren und dann rückwärts zu optimieren, um Systeme dort zu optimieren, wo es wirklich darauf ankommt. Der Leistungsprofiler in den Chrome-Entwicklertools ist für diesen Schritt von unschätzbarem Wert und hat mir geholfen, einige größere Probleme in früheren Versionen des Spiels zu diagnostizieren. Ihre Zeit als Entwickler ist wertvoll. Achten Sie also darauf, dass Sie keine Zeit damit verbringen, über Probleme nachzudenken, die sich als unwichtig oder redundant erweisen könnten.
User Experience überwachen
Bei der Anwendung all dieser Tricks sollten Sie darauf achten, dass das Spiel auch in freier Wildbahn erwartungsgemäß funktioniert. Bei jeder Spieleentwicklung ist es unerlässlich, eine Reihe von Hardwarefunktionen zu berücksichtigen. Webspiele können jedoch ein viel breiteres Spektrum abdecken, das sowohl Top-Desktops als auch jahrzehntealte Mobilgeräte gleichzeitig umfasst. Die einfachste Möglichkeit besteht darin, Einstellungen zum Anpassen der wahrscheinlichsten Engpässe in Ihrer Codebasis – sowohl für GPU- als auch CPU-intensive Aufgaben – anzubieten, wie von Ihrem Profiler aufgedeckt.
Die Profilerstellung auf Ihrem eigenen Computer kann jedoch nur so viel abdecken, daher ist es sinnvoll, die Feedbackschleife mit Ihren Nutzern zu schließen. Für Slow Roads führe ich einfache Analysen durch, die über die Leistung und kontextabhängige Faktoren wie die Bildschirmauflösung berichten. Diese Analysen werden zusammen mit dem schriftlichen Feedback, das der Nutzer über das In-Game-Formular sendet, mithilfe von Socket.io an ein einfaches Knoten-Back-End gesendet. Anfangs wurden bei diesen Analysen viele wichtige Probleme erkannt, die mit einfachen Änderungen an der UX behoben werden konnten. Beispielsweise wurde das Einstellungsmenü hervorgehoben, wenn ein konstant niedriger fps erkannt wird, oder eine Warnung, dass Nutzer bei besonders schlechter Leistung möglicherweise die Hardwarebeschleunigung aktivieren müssen.
Langsame Straßen auf deinem Weg
Auch nach all diesen Maßnahmen muss ein erheblicher Teil der Spieler mit niedrigeren Einstellungen spielen – hauptsächlich auf Geräten, die schlanke Geräte ohne GPU verwenden. Auch wenn die verfügbaren Qualitätseinstellungen zu einer ziemlich gleichmäßigen Leistungsverteilung führen, erreichen nur 52% der Spieler mehr als 55 fps.
Glücklicherweise gibt es noch viele Möglichkeiten, die Leistung zu senken. Neben weiteren Rendering-Tricks zur Verringerung der GPU-Nachfrage hoffe ich, in naher Zukunft mit Web Workern zu experimentieren, um die Umgebungsgenerierung zu parallelisieren. Möglicherweise muss WASM oder WebGPU in die Codebasis eingebunden werden. Wenn ich mehr Spielraum habe, schaffen wir mehr Umgebung mit vielfältigeren Optionen.
Mit „Slow Roads“ können wir zeigen, wie aufwendig, leistungsstark und populär Browserspiele sein können. Wenn es mir gelungen ist, Ihr Interesse an WebGL zu wecken, sollten Sie wissen, dass technologisch Slow Roads ein relativ oberflächliches Beispiel für die vollen Möglichkeiten ist. Ich empfehle Ihnen dringend, sich den Three.js-Schaufensterbereich anzusehen. Besonders Interessierte an der Entwicklung von Webspielen sind in der Community unter webgamedev.com herzlich willkommen.