Ein neues Spiel mit einer alten Atmosphäre auf Three.js

Es gibt viele Fans alter Spiele. Und sie sind nicht abgeneigt, eine geizige nostalgische Träne herauszulassen und nein, nein, sondern spielen "Arkanoid", "Pakman" oder "Prince of Persia" wie vor zwanzig, dreißig, vierzig oder - ersetzen Sie die gewünschte Anzahl - vor Jahren. DOS-Box und Emulatoren - um ihnen zu helfen. Ja, was ist da? Ich habe kürzlich einen Stream des allerersten 2D-Prinzen von Persien auf YouTube gesehen, in dem ein ziemlich junger Streamer, nachdem er ein anderes tödliches Hindernis passiert hatte und ihm mit der Hand den Schweiß von der Stirn winkte, sagte: „Ich war noch nie in einem Computerspiel so verängstigt ". Das heißt, auch junge Leute können den Hardcore und die Coolness alter Spiele schätzen.


Ich dachte, warum nicht ein neues Spiel in einem ähnlichen Stil erstellen? Ja, es gibt verschiedene Remakes und Klone. Auch moderne Spiele im Stil der Pixelkunst erfreuen. In der Regel wiederholen jedoch alle Quests, Mechaniken und manchmal sogar das vollständige Design der alten Spiele, auf deren Grundlage sie erstellt wurden. Nun, oder umgekehrt, sie bieten eine völlig neue Handlung und Orte, was einfach eine visuelle Stilisierung von "antik" ist. Aber was ist, wenn Sie sich vorstellen, was der neue Teil des alten Spiels wäre, wenn er dem letzten Teil der Serie folgen würde? Ich habe beschlossen, eine zu erstellen.

Ich nahm einen 2D-Plattformer und fügte dort 3D-Grafiken hinzu, wobei die klassische Seitenansicht beibehalten wurde. Er baute neue Labyrinthe, entwickelte neue Quests, führte Aufgaben ein und implementierte Inventarslots. Das Raumgefühl wurde leicht abwechslungsreich, indem 90-Grad-Drehungen hinzugefügt wurden. Vielleicht könnte in jenen alten Zeiten, vor dem vollständigen Übergang von Spielen in 3D mit drei Freiheitsgraden, so etwas herauskommen.

Da ich gerne Spiele für Browser schreibe, habe ich beschlossen, ein Spiel für den Browser zu erstellen. Aufgrund seiner Spezifität gibt es keine verrückte Anzahl von Polygonen, die zumindest gleichzeitig auf dem Bildschirm angezeigt werden. Es gibt keine offene Welt. Daher wird der Browser dadurch nicht stark belastet. Um 3D-Grafiken anzuzeigen, habe ich meine Lieblingsbibliothek Three.js (WebGL) mit meinem eigenen Wrapper ausgewählt. Es werden keine anderen Bibliotheken verwendet und der Code ist in reinem Javascript geschrieben.

Three.js und die Probleme, auf die ich gestoßen bin


Entwickler von 3D-Grafik-Engines veröffentlichen regelmäßig, etwa alle paar Jahre, neue Versionen ihrer Kreationen. Im Fall der Three.js-Skriptbibliothek begeistern uns Entwickler mit Updates in rasender Häufigkeit - etwa einmal im Monat. Zum Zeitpunkt des Schreibens hatte die neueste Version die Nummer 106. Es scheint, dass dies in Ordnung ist. (Nein.)

Warum denkt niemand an Abwärtskompatibilität? Als die Entwickler von Three.js die Umgebung der Materialeigenschaften umbenannten, wurde mir klar: „Houston, wir haben Probleme . Okay, sagen wir, es war nicht schwierig für mich in meinem gesamten Code, das Wort "Umgebung" mit einem Such-Ersetzen in "Farbe" zu ändern. Aber als sie die Möglichkeit ausschlossen, dass eine Lichtquelle einen Schatten ohne Beleuchtung als solche erzeugt, wurde mir klar, dass die Landung des Adlers jetzt in wirklicher Gefahr ist und die Basis der Ruhe sich langsam in die Basis der Tollwut verwandelt. Wie sich später herausstellte, war es jedoch nicht so schlimm ...

Tatsache ist, dass in meinem Spiel neben gebackenem Licht auch dynamisches Licht verwendet wird. Die Anzahl der dynamischen Quellen in einer 3D-Szene wirkt sich stark auf die Leistung aus.


Ich habe die Anzahl der Quellen in den Spieleinstellungen ausgewählt - von 1 bis 7. Und je nach Leistung der Grafikkarte können Sie verschiedene Werte ausprobieren.

Es gibt auch ein Array, das die Koordinaten und Eigenschaften jeder Quelle enthält - Intensität, Farbe usw. Das funktioniert so. Wenn Sie sich auf einem bestimmten Feld um den Spieler herum in der Spielwelt bewegen, leuchtet die Anzahl der Lichtquellen, die in den Einstellungen mit den angegebenen Eigenschaften angegeben sind. Das heißt, die Lichtquellen scheinen dem Spieler (um ihn herum) durch die Wolke zu folgen.

Also, Houston, was sind unsere Probleme? Ich berichte.

Problem Nummer 1: Schatten


Lichtquellen erzeugen Schatten. Mir ist aufgefallen, dass Schatten von Punktlichtquellen viel mehr Ressourcen verbrauchen als gerichtete Schatten (Punktlicht). Und das ist verständlich: Bei einer Punktquelle werden Schatten in alle Richtungen und direktional geworfen - nur in einem bestimmten Kegel. Ich schlug jedoch vor, dass, wenn Schatten in alle Richtungen nicht benötigt werden, zwei Quellen gleichzeitig verwendet werden können: Ein Punkt erzeugt nur Beleuchtung und ein gerichteter Punkt erzeugt nur Schatten in einer bestimmten Richtung. Dies ist perfekt für mein Spiel und spart, wie sich herausstellte, erheblich Rechenleistung.


Die Pyramide begrenzt den Bereich, in dem Schatten auftreten können.

Aber hier ist das Problem: Ab Version drei.js r73 kann eine gerichtete Lichtquelle nicht mehr nur einen Schatten werfen, sondern gibt jetzt auch immer Licht. Und das Licht davon breitet sich innerhalb des Gebiets aus, ebenso wie der Schatten. Es ist unmöglich, eine Punktquelle zu entfernen und nur eine Richtungsquelle zu belassen: Ich brauche Beleuchtung in alle Richtungen. Und beide Quellen zu verwenden, um sie auf die gewünschte Helligkeit einzustellen, funktioniert auch nicht: Dann werden die Objekte im Kegel viel heller beleuchtet. Die Verwendung von "ehrlicher" Beleuchtung mit Schatten in alle Richtungen wirkt sich nur tödlich auf die Leistung des Spiels aus.

Und sie haben die notwendige Eigenschaft einer gerichteten Lichtquelle .onlyShadow entfernt, nur weil der Autor der Engine dies wollte.

Problem Nummer 2: Eigenschaftsnamen


Ich entschied, dass ich lieber bei der r71-Version bleiben möchte, bei der die benötigte Beleuchtungseigenschaft noch vorhanden ist. Warum dann nicht r72? Immerhin ist die Eigenschaft erst in Version r73 verschwunden. Weil ich schon ziemlich viel Code zum Laden von 3D-Modellen, Animationen und Physik für die r71-Version geschrieben habe. In der r72-Version hat sich eine große Anzahl von Eigenschaftsnamen geändert: type - shadowMap wurde zu shadow.map usw. Im Allgemeinen wollte ich das alles auch nicht umbenennen. Und der Unterschied zwischen einer Version ist gering. Wir bleiben also bei der r71-Version.

Problem Nummer 3: Braten von Schatten



Gebackenes Licht in verschiedenen Versionen des Motors sieht auch anders aus. Ich weiß nicht, was sie dort mit ihm machen, aber wenn eine Schattenkarte auf die Textur angewendet wird, ändern sich die Helligkeit und sogar die Farbe dramatisch. Im Allgemeinen habe ich dieses Problem irgendwie gelöst, indem ich die Engine in GLSL-Code gesteckt und die automatische Farbkorrektur im Loader von 3D-Modellen an den Stellen eingestellt habe, an denen Schattenkarten verwendet werden. Dies funktioniert natürlich nur für Version r71. Für andere Versionen müssen Sie einige andere Parameter verwenden. Dies ist ein weiterer Grund, auf der r71-Version zu bleiben.


Eigentlich macht es keinen Sinn, auf die neuesten Versionen umzusteigen, da sich das Ergebnis der Arbeit im Vergleich zur alten nicht grundlegend in der Leistung unterscheidet.

Gut. Eine gerichtete Lichtquelle erzeugt nur einen Schatten. Ich kann Schatten backen und in die Baugruppe der Version r71 laden. Und jetzt - der wichtigste Hinterhalt. Charaktere.

Problem 4: Skelettanimation


Darum habe ich sehr lange gekämpft. Insgesamt habe ich ein paar Wochen lang versucht, einen funktionierenden Algorithmus für die Übertragung eines Charakters mit einer Reihe von Animationen in das Spiel zu finden.


Neuere Versionen von Three.js haben dieses Problem nicht. Dort habe ich erfolgreich ein animiertes Charaktermodell in einem bekannten Onlinedienst erstellt, es in das beliebte gltf-Format konvertiert und in die Testszene hochgeladen. Wenn jedoch die neuen Versionen von Three.js das Format gltf 2.0 verwenden, unterstützt meins nur 1.0. Es scheint, was ist das Problem? Konvertieren Sie auf 1.0 und laden Sie es herunter. Es stellte sich nicht so einfach heraus. Anscheinend gibt es verschiedene Variationen des gltf 1.0-Formats. Ich brauchte eine, in der zusätzlich zur Hauptmodelldatei zwei weitere Dateien mit der Erweiterung * .glsl vorhanden sein sollten. In diesem Fall kann das Format der Hauptdatei jedoch variieren ... Im Allgemeinen konnte ich keinen Konverter finden, der alle Parameter erfüllt, zumal er auch vom neuen in das alte Format konvertiert wurde. Ich konnte auch die alte Version von three.js für die Unterstützung von gltf 2.0 nicht beenden: Diese Unterstützung ist zu tief im Code verankert, in der Mathematik verwurzelt und in verschiedenen Versionen der Engine unterschiedlich implementiert ... Im Allgemeinen hat es mit gltf irgendwie nicht geklappt.

Ich habe versucht, das .dae-Format für 3D-Modelle zu verwenden. Infolgedessen wurde das Modell selbst geladen, aber ich konnte die Charakteranimationen nicht richtig funktionieren lassen. Es gab auch Probleme mit Texturen.

Mit dem .md2-Format hat es sich als gut herausgestellt. Der Charakter wurde sofort angezeigt und alle Animationen funktionierten korrekt. Aber so wie ich es verstehe, ist md2 ein Modellformat für Quake 2 und jemand hat seine Unterstützung in Three.js nur zum Spaß implementiert. Und wo man Modelle in diesem Format bekommt und noch mehr, um meinen eigenen Charakter darin zu entwerfen und zu speichern, habe ich nicht gefunden.

Ich habe noch ein paar Formate ausprobiert. Aber unter ihnen gab es keinen Bootloader für three.js, es gab keine Programme, um die Erhaltung ihres Charakters in ihnen zu schaffen, dann unterstützten sie überhaupt keine Skelettanimation.

Die Basis "Tollwut" war genau richtig, um die Basis "Verzweiflung" umzubenennen.

Ich war schon fast verzweifelt und dachte darüber nach, auf Kosten der Leistung (die Geschichte einer gerichteten Lichtquelle) auf eine neue Version des Motors umzusteigen. Zum letzten Mal entschied ich mich, das JSON-Format zu quälen, mit dem ich tatsächlich angefangen hatte. Zum ersten Mal konnte ich den Vorgang des Konvertierens eines Zeichens aus einer Beispieldatei, die im Three.js-Bundle enthalten war, vom ursprünglichen .blend-Format in .json im Blender 3D-Editor nicht wiederholen. Die Animationen wurden verdorben und verhielten sich zufällig, und mit einem Dutzend Variationen des Exporteurs von Blender bis Json funktionierte jeder einzelne falsch. Außerdem habe ich sie auch auf mehreren Versionen von Blender ausprobiert. JSONLoader selbst, dh ein Modelllader im JSON-Format, wurde jetzt aus three.js entfernt. Ich beschloss zu prüfen, welche Version seine Unterstützung eingestellt hatte, um ihn und ein Beispiel des 3D-Modells nicht aus meiner Version zu nehmen, sondern aus der, in der er sich noch befand. Sie stellte sich als r88 heraus. Und siehe da! Ich habe es geschafft, den Export des Testmodells in r71 zu reproduzieren und alles, einschließlich Charakteranimationen, hat im Spiel gut funktioniert!

"Eagle" landete sicher.

Dann habe ich beschlossen, eine der Animationen des Testcharakters in Blender zu bearbeiten, um zu sehen, ob ich meine eigene erstellen kann. Hier wartete ein Mist auf mich. Die Animation, die ich bearbeitet habe, wollte im Spiel überhaupt nicht funktionieren. Der Charakter erstarrte in der Ausgangsposition. Obwohl andere Animationen ohne Probleme funktionierten. Aber das ist etwas. Das heißt, jetzt ist das Problem meine Unkenntnis einiger Nuancen beim Bearbeiten der Animation.

Dann dachte ich - was ist, wenn ich den Autor dieses Beispiels frage, wie er es gemacht hat? Aber zum Autor zu gelangen war nicht einfach. Er hat keine persönlichen Kontakte bei Github. Eine Suche nach einem Spitznamen und ein paar weiteren Parametern brachte zu seinem Twitter, jedoch wurden private Nachrichten dort geschlossen. Aber von dort erfuhr ich, dass er Professor an einer amerikanischen Universität war, und ging auf die Website dieser Bildungseinrichtung. Es stellte sich heraus, dass der Professor mit seinen Studenten 3D-Grafiken beschäftigte und sein Beispiel als methodisches Material verwendete. Dann kehrte ich nach Github zurück und sah mir alle Repositories an. Hier habe ich auf Erfolg gewartet. Wie ich erwartet hatte, wurde sein Beispiel in einem separaten Repository gespeichert. Und nicht nur eine Kopie dessen, was ich bereits in den Beispielen von three.js gesehen habe, sondern ein Beispiel, das sorgfältig mit Anweisungen versehen wurde (anscheinend für Studenten). Ich habe das Archiv heruntergeladen und gemäß den Anweisungen alles wiederholt. Hurra!

Dies ist ein kleiner Schritt für die Menschheit, aber ein großer Sprung für eine Person und ihr Spiel!

Kein Problem, Houston!



Jetzt verstehe ich, dass ich, wenn ich mich an dieses Format, diese Anweisungen und diese Versionen von Three.js, JSONLoader und Blender halte und alles auf die gleiche Weise mache, jeden meiner Charaktere erstellen und in das Browsergame laden kann. Die gute Nachricht war, dass Sie trotz der Verwendung der alten Version der Engine die neueste Version des 3D Blender-Editors verwenden und beliebige Charaktere mit Animation erstellen können. In diesem Moment müssen Sie sie nach einem genau definierten Schema mit diesem speziellen Toolkit exportieren.

Ja, ich habe ein weiteres Problem mit der neuen Version von Three.js festgestellt: Während des Spiels werden beim Scrollen des Bildschirms aus irgendeinem Grund ständige Friese beobachtet. Dies ist nicht auf einen erhöhten Ressourcenverbrauch zurückzuführen - der Prozessor und die Grafikkarte werden nicht zu 100% geladen. Und im alten R71 gibt es keine solche Schande.

Jetzt bleibt es nur noch für das Spiel im 3D-Charakter-Editor mit Animation zu tun. Und natürlich die Geometrie der Ebenen. Ich weiß nicht, wie viel Zeit ich dafür brauchen werde. Bisher habe ich jedoch gerade eine kostenlose Demoversion auf Webkit gesammelt und in beliebte Anwendungsspeicher hochgeladen.

Ein bisschen über das Spiel


Name. Ich habe das Spiel Percy Lancaster genannt. Hier ist alles klar: "Ich bin Künstler und ich sehe es."

Wie Grafiken erstellt wurden . Ich habe zwei Ebenen im 3D-Editor erstellt, sie in einem Abstand voneinander angeordnet, sodass die entfernte hinter der nächsten verborgen war, eine Steinstruktur darauf gezogen, Löcher in die nächste geschnitten und dann die unnötigen, dh unsichtbaren Teile entfernt. Dann modellierte er die Flugzeuge für Böden und Decken. Es stellte sich also heraus, dass es sich um einen Korridor handelte, in dem ein Spieler laufen konnte. Ich habe Orte mit verschiedenen Texturen diversifiziert.


Im Spiel selbst gibt es keine solche, es gibt nur eine Seitenansicht und es sind keine schwarzen Räume erkennbar. Dies ist nur eine allgemeine Ansicht des Korridors.

Ich beschloss, sich kreuzende Korridore zu schaffen. Während des Abbiegens wird eine Wand des anderen Korridors fertiggestellt, die gesamte Struktur wird gedreht, und dann wird die Wand des ersten Korridors entfernt. Basierend auf dieser Mechanik plane ich auch Türme zu erstellen, in denen Sie die Wendeltreppe erklimmen können.


Mit statischen Grafiken gibt es in der Tat keine Probleme, es kann einfach von jedem 3D-Editor nach json exportiert werden. Schwierigkeiten traten, wie bereits erwähnt, nur bei Backschatten und Charakteranimationen auf.

Leistung. Bei der HD-Bildschirmauflösung, dh 1280 x 720, ist meine nicht so leistungsstarke GT-730-Grafikkarte zu etwa 35-40% und der Xeon E5440-Prozessor zu etwa 30% ausgelastet. Ich denke, das ist mehr als ein akzeptables Ergebnis.

Betriebssystem Bisher ist die Demo nur unter Windows als Assembly auf Webkit verfügbar. In Zukunft plane ich eine Browserversion zu starten. Ich bin auf die Tatsache gestoßen, dass nicht alle Browser reibungslos über den Bildschirm scrollen. Ich muss noch daran arbeiten, die Grafikausgabefunktion zu verwalten und aufzurufen. In der Zwischenzeit habe ich mich für Webkit Version 26.0 entschieden. Sie wiegt wenig und alles funktioniert gut mit ihr.

Der Ton. Sounds werden teilweise aus freien Bibliotheken entnommen, teilweise generiert. Im Allgemeinen wurden sie bisher zum „Sein“ gemacht. Ich habe mich zwar nicht viel um sie gekümmert.

Video Demo-Level mit voller Passage.


Pläne


Ich habe vor, zum Crowdfunding zu gehen und mit dem gesammelten Geld einen 3D-Modellierer einzustellen, der einen normalen Charakter und Animationen für ihn schafft. Trotzdem sind die Grafiken nicht meine. Aber jetzt weiß ich, wie ich einen Charakter in mein Spiel einführen kann.

Außerdem plane ich, eine Browserversion für die neuesten Versionen von Chrome und Firefox zu starten. Lassen Sie mich Ihnen ein Geheimnis verraten, ich habe es sogar geschafft, das Spiel auf MS Edge auszuführen, aber aus irgendeinem Grund gab es keine Objekte, auf denen sich die Texturen gebackener Schatten überlappten. Ich habe es immer noch nicht herausgefunden. Als nächstes werde ich für Browser für Linux und Android debuggen, und wenn ich Apple-Geräte zum Testen bekomme, dann auch für Browser unter iOS und MacOS.

Auf lange Sicht - Schreiben einer eigenen Bibliothek für die Arbeit mit WebGL wie Three.js: Ich mag es nicht, dass sie Eigenschaften abrupt umbenennen und die Funktionen entfernen, die ich benötige. Umgekehrt brauche ich die meisten der vielen Möglichkeiten, die Three.js bietet, nicht.

Ich denke später, wenn ich die korrekte Funktionsweise des Spiels für alle Browser auf verschiedenen Betriebssystemen herausfinde, werde ich einen Artikel darüber mit einigen technischen Details schreiben.

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


All Articles