Entwicklerblog | Volle Ladung

Im neuesten Beitrag unserer NXT-Entwicklerblogreihe erzählt uns Mod Dunk über die Änderungen, die nötig waren, um beim neuen RuneScape-Client einen flüssigen Bewegungsablauf zu erreichen.


RuneScape ist ein riesiges Spiel, das bis an den Rand voll ist mit Charakteren, Abenteuern und Gebieten, die man erkunden kann. 15 Jahre Inhalte wiegen schwer auf den Schultern des aktuellen Java-Clients, weshalb euch NXT eine moderne Engine bringt, die – jetzt und in Zukunft – effizient mit der schieren Größe von RuneScape umgehen kann.

Ladezeit ist in der Spielentwicklung immer ein Problem – besonders bei offenen Spielwelten wie RuneScape. Da Spieler in jede Richtung gehen können und das Gebiet zu groß ist, um alles zu speichern, muss das Spiel Ziele fließend laden, ohne das Spielerlebnis zu unterbrechen.

Ihr denkt wahrscheinlich jetzt: „Moment, ich bin mir ziemlich sicher, dass RuneScape das Spiel pausiert, um zu laden.“ – und da habt ihr recht. Bestimmt ist jeder RuneScape-Spieler mit der Benachrichtung ‚Lädt, bitte warte‘ vertraut, die das Spiel pausiert, während ihr euch durch das Spiel bewegt.

Diese Benachrichtigung wird bei NXT nicht mehr benötigt – und ich werde euch erklären, wie wir das angestellt haben.

Streaming

Die Welt von RuneScape ist in Kartenabschnitte unterteilt. Wenn ihr euch bewegt, werden die Felder vor eurem Charakter geladen, während die hinter euch entfernt werden, um Speicher zu sparen. Der Java-Client unterbricht das ganze Spiel, während er das tut, deshalb gibt es die Pausen.

Eines unserer Hauptziele bei NXT war es, diese Ladezeit im Hintergrund ablaufen zu lassen, ohne das Spielerlebnis zu unterbrechen. Die Technik, die dies ermöglich, heißt ‚Streaming‘ oder ‚asynchrones Laden‘, weil sie parallel zum Hauptprozess des Spiels abläuft.

Um das zu tun, fassen wir jede Ladeanfrage in ein ‚Job‘-Objekt zusammen, und teilen jeden Job einem von einer Reihe an „worker threads“ zu. Diese Threads verarbeiten diese Jobs dann selbst und geben die Ergebnisse zurück, wenn sie fertig sind.

Dies hat leider erfordert, dass alle unserer 3D-Modelle, Animationen, Texturen, Partikelsysteme, skriptbasierte Objekte – einfach alles im Spiel – neu geschrieben werden musste. Wie ihr euch wahrscheinlich vorstellen könnt, war das eine riesige Aufgabe, sie war aber definitiv jede Mühe wert, denn wir können dadurch eine fließende Bewegung durch die Welt bieten, die mit dem Java-Client unmöglich wäre!

Wassereffekte

Beachtet, wie sich das Licht ändert, wenn wir zu einem neuen Kartenabschnitt gehen.

Packen

Viele Fragmente der Welt schnell laden zu können, ist eine wesentliche Voraussetzung, um unsere größere Sichtweite bei NXT ermöglichen zu können.

Die maximale Sichtweite bei NXT ist mehr als viermal so weit wie bei Java und das bedeutet, dass NXT ungefähr sechzehnmal so viele Modelle gleichzeitig laden muss. Parallel zu laden, ist eine Sache, aber diese Anzahl bedeutet, dass wir jedes einzelne Element etwas schneller laden müssen.

Bei jedem Modell, das Java lädt, führt es einen aufwändigen Verarbeitungsprozess aus, um die nötigen Informationen für sein Rendering zu berechnen. Wie bei jedem Spiel besteht jedes 3D-Modell aus Dreiecken, die seine sichtbare Oberfläche darstellen. Die 3 Eckpunkte jedes Dreiecks enthalten jeweils Informationen zur Oberfläche, der Farbe und der Textur.

Beim Java-Client werden Algorithmen benutzt, um diese Werte zu berechnen, was sehr langsam sein kann. Bei NXT werden diese hingegen in einer externen Packphase im Voraus berechnet und direkt während der Laufzeit des Spiels gelesen. Dies beschleunigt die Ladezeit beträchtlich (vergrößert allerdings auch die Ressourcen, die heruntergeladen werden müssen) und ist, wo auch immer möglich, unsere bevorzugte Methode.

In ähnlicher Weise werden auch Texturen in einer externen Packphase komprimiert, wodurch weniger Daten während der Laufzeit des Spiels verarbeitet werden müssen.

“Java-Sichtweite" “NXT-Sichtweite"

Bild oben: Java-Sichtweite. Bild unten: NXT-Sichtweite.

Vorladen

Beim Java-Client wird sogar ein Lade-Bildschirm benötigt, um zum Login-Bildschirm zu kommen. Bei NXT wurde diese Zeit deutlich gekürzt. Wir führen jetzt viel mehr Arbeit durch, während Spieler in der Lobby sind. In dieser Phase macht der Computer nichts anderes, als das Menü zu rendern, also haben wir Zeit übrig, um so viel wie möglich vorzuladen.

Hauptsächlich laden wir die Welt vor. Wenn ihr euch zum Beispiel in Lumbridge ausgeloggt habt, wird der Client dieses Gebiet im Hintergrund vorladen, da wir bereits wissen, dass ihr euch dort befinden werdet, wenn ihr in die Spielwelt zurückkehrt.

Etwas anderes, das wir an diesem Punkt erledigen können, ist das Vorladen der Shader (Schattierer). Shader sind kleine Programme, die auf der Grafikkarte laufen und dort Render-Berechnungen ausführen. Bei NXT verwenden wir eine Liste an häufig verwendeten Shadern und stellen sicher, dass sie verfügbar sind, bevor das Spiel beginnt.

Detaillierungsgrad der Kartenabschnitte

Bei NXT optimieren wir die Ladezeit für entfernte Kartenabschnitte, indem wir nur die wichtigsten Objekte rendern. Zum Beispiel sollten die Mauern eines Gebäudes immer aus der Ferne sichtbar sein, aber die Details im Inneren müssen erst geladen werden, wenn man nahe genug dran ist, um hineinsehen zu können. Dieses System des Detaillierungsgrads beschleunigt die Lade- und Renderzeit sehr effizient, wenn die Sichtweite so groß ist.

Armadyl's Tower

Große Objekte, wie zum Beispiel Armadyls Turm, werden auch von weit weg geladen, aber nicht kleine Details wie das Gras oder die Blumen um den Turm herum.

Shader-Permutation

Die Welt von RuneScape besteht aus vielen Objekten, die aus verschiedensten Materialien hergestellt sind. Jedes Material kann eine Kombination aus visuellen Effekten verwenden, wie Environment Mapping, Texturen, Glanzlichter, Alpha-Transparenz, Emissionsbelichtung und mehr.

Bei NXT haben wir weitere Features eingeführt, wie Hardware-Animation, verbesserte Nebeleffekte und Shadow Mapping. Da wir es ermöglichen, jedes Feature einzeln zu aktivieren, werden mehrere hunderte oder sogar tausende Shader-Permutationen (oder Varianten) generiert. Zum Glück konnten wir diese Zahl auf ungefähr 100 reduzieren und haben weiteren Code verfügbar, um sie in der Entwicklung zu reduzieren.

Das erreichen wir auf folgende Weise:
  • Wir identifizieren die Shader-Features, die die meisten Permutationen produzieren, indem wir unser hauseigenes Shader-Fehlerbehebungssystem anwenden.
  • Ausgewählte Features, die günstig in der Verwendung sind, sind einfach immer verfügbar.
  • Ausgewählte Features, die teuer in der Verwendung sind, können so verändert werden, dass sie sich selbst im Shader-Programm ein- oder ausschalten können.

Ein großes Problem bei generischen Spiel-Engines wie Unreal oder Unity ist, dass Benutzer leicht verschiedene Materialien herstellen können, die viele tausende Shader-Permutationen erfordern, was die Ladezeit für all diese stark erhöht. Da NXT nur einen sehr konkreten (wenn auch sehr großen) Datensatz bearbeiten muss, können wir unseren Rendering- und Shader-Code optimieren und effizienter gestalten, indem weniger Permutationen generiert werden.

All diese Arbeit hat uns einem würdigen Ziel entgegengebracht – sicherzustellen, dass der NXT-Client viel mehr von der Welt von RuneScape zeigen kann und dabei die Ladezeit des Java-Clients trotzdem unterbietet.

Mod Dunk
Senior-Engine-Entwickler

Zurück nach oben