Dieses Mal präsentiere ich Ihnen das JS-imitierende Legend of Legend-Spiel. Was sind die Vorsichtsmaßnahmen für das JS-imitierende Legend of Legend-Spiel?
Vorwort
Die erste Version des Spiels wurde 2014 entwickelt. Der Browser verwendet HTML+CSS+JS und der Server ASP+ PHP, die Kommunikation verwendet Ajax und die Datenspeicherung verwendet Access+MySql. Aufgrund einiger Probleme (damals wusste ich nicht, wie man Node verwendet, war es wirklich schwierig, komplexe Logik in ASP zu schreiben; zu dieser Zeit gab es nur wenige Texte auf der Leinwand und das Dom-Rendering konnte leicht zu Leistungsengpässen führen). , es wurde aufgegeben. Später wurde eine Version mit Leinwand neu angefertigt. Dieser Artikel wurde im Jahr 2018 verfasst.
1. Vorbereitung vor der Entwicklung
Warum Javascript verwenden Um ein relativ komplexes PC-seitiges Spiel zu implementieren
1.js ist es möglich, PC-seitige Online-Spiele zu implementieren. Mit der Aktualisierung der Hardwarekonfigurationen von PCs und Mobiltelefonen, der Aktualisierung von Browsern und der Entwicklung verschiedener H5-Bibliotheken wird es immer schwieriger, ein Online-Spiel in js zu implementieren. Die Schwierigkeit liegt hier hauptsächlich in zwei Aspekten: der Leistung des Browsers; ob der js-Code einfach genug zu erweitern ist, um die Iteration eines Spiels mit äußerst komplexer Logik zu erfüllen.
2. Zu diesem Zeitpunkt gibt es unter den js-Spielen nur wenige als Referenz. Die meisten (fast alle) Spiele mit Multiplayer-Verbindungen, serverseitiger Datenspeicherung und komplexen Interaktionen werden mit Flash entwickelt. Aber Flash ist schließlich auf dem Rückzug, während sich JS rasant weiterentwickelt und laufen kann, solange es einen Browser gibt.
Warum haben Sie sich für ein legendäres Spiel aus dem Jahr 2001 entschieden?
Der erste Grund ist natürlich das Gefühl für das alte Spiel; der andere, wichtigere Grund ist, dass... . Entweder kann ich das Spiel nicht spielen, oder ich kann es spielen, habe aber nicht die Materialien (Bilder, Soundeffekte usw.). Ich denke, es ist Zeitverschwendung, sich viel Mühe zu geben, Karten, Charakter- und Monstermodelle, Gegenstände und Ausrüstungsdiagramme eines Spiels zu sammeln und sie dann zu verarbeiten und zu analysieren, bevor sie für die JS-Entwicklung verwendet werden.
Da ich bereits einige Materialien für Legend-Spiele gesammelt und glücklicherweise eine Möglichkeit gefunden habe, die Legend of Legend-Client-Ressourcendateien (Github-Adresse) zu extrahieren, kann ich direkt mit dem Schreiben von Code beginnen, was etwas Vorbereitungszeit spart.
Mögliche Schwierigkeiten
1. Browserleistung: Dies sollte der schwierigste Punkt sein. Wenn das Spiel 40 Frames beibehalten möchte, bleiben für jeden Frame nur noch 25 ms für die Berechnung durch js übrig. Und da das Rendern in der Regel mehr Leistung verbraucht als das Berechnen, beträgt die tatsächlich verbleibende Zeit für js nur etwa 10 Millisekunden.
2. Anti-Cheating: Wie kann verhindert werden, dass Benutzer Schnittstellen direkt aufrufen oder Netzwerkanforderungsdaten manipulieren? Da das Ziel darin besteht, mit js komplexere Spiele zu implementieren, und jedes Online-Spiel dies berücksichtigen muss, muss es eine relativ ausgereifte Lösung geben. Dies ist nicht der Schwerpunkt dieses Artikels.
2. Gesamtdesign
Browserseite
Die Bildschirmdarstellung verwendet Canvas.
Im Vergleich zu dom(p)+css kann Canvas eine komplexere Szenendarstellung und Ereignisverwaltung bewältigen. Die folgende Szene umfasst beispielsweise vier Bilder: Spieler, Tiere, Gegenstände auf dem Boden und das unterste Kartenbild . (Es gibt tatsächlich Schatten auf dem Boden, die entsprechenden Namen, die erscheinen, wenn die Maus auf Charaktere, Tiere und Objekte zeigt, sowie Schatten auf dem Boden. Aus Gründen der Lesbarkeit werden wir nicht so viel Inhalt berücksichtigen.)
Wenn Sie zu diesem Zeitpunkt den Effekt „Klicken Sie auf das Tier, greifen Sie das Tier an; klicken Sie auf den Gegenstand, nehmen Sie den Gegenstand auf“ erzielen möchten, dann benötigen Sie um die Ereignisse von Tieren und Gegenständen zu überwachen. Wenn Sie die Dom-Methode verwenden, treten mehrere Probleme auf, die schwer zu lösen sind:
a Die Reihenfolge des Renderns unterscheidet sich von der Reihenfolge der Ereignisverarbeitung (manchmal ist die kleinere z -index muss als erstes Ereignis verarbeitet werden), was eine zusätzliche Verarbeitung erfordert. Im obigen Beispiel ist es beispielsweise einfach, auf Charaktere zu klicken, wenn Sie auf Monster oder Gegenstände klicken. Daher müssen Sie für die Charaktere eine „Klickereignisdurchdringung“-Verarbeitung durchführen. Darüber hinaus ist die Reihenfolge der Ereignisverarbeitung nicht festgelegt: Wenn ich über eine Fähigkeit verfüge (z. B. Behandlung im Spiel), die die Freilassung eines Charakters erfordert, muss der Charakter zu diesem Zeitpunkt über eine Ereignisüberwachung verfügen. Ob ein Element Ereignisse verarbeiten muss und in welcher Reihenfolge Ereignisse verarbeitet werden, hängt daher vom Spielstatus ab, und die Ereignisbindung von DOM kann die Anforderungen nicht mehr erfüllen.
b. Verwandte Elemente lassen sich nur schwer im selben Dom-Knoten platzieren: wie das Modell des Spielers, der Name des Spielers und die Fähigkeitseffekte des Spielers. Idealerweise sollten sie in einem
-Container zur einfachen Verwaltung (auf diese Weise kann die Positionierung mehrerer Elemente vom übergeordneten Element geerbt werden, ohne dass die Position separat behandelt werden muss). Auf diese Weise wird es jedoch schwierig sein, mit dem Z-Index umzugehen. Wenn sich beispielsweise Spieler A über Spieler B befindet, wird A von B verdeckt. Daher muss der Z-Index von A kleiner sein, aber der Name von Spieler A darf nicht durch den Namen oder Schatten von B verdeckt werden, was nicht erreicht werden kann . Vereinfacht ausgedrückt wird die Wartbarkeit der DOM-Struktur den Effekt der Bildschirmanzeige beeinträchtigen und umgekehrt.
c. Leistungsprobleme. Selbst wenn der Effekt geopfert wird, führt die Verwendung von DOM zum Rendern unweigerlich zu vielen verschachtelten Beziehungen, und die Stile aller Elemente ändern sich häufig, was ständig ein Repaint oder sogar einen Reflow des Browsers auslöst.
Trennung der Canvas-Rendering-Logik und der Projektlogik
Wenn die verschiedenen Rendering-Vorgänge von Canvas (z. B. drawImage, fillText usw.) mit dem Projektcode kombiniert werden, führt dies unweigerlich dazu die Unfähigkeit, das Projekt später aufrechtzuerhalten. Nachdem ich mehrere vorhandene Canvas-Bibliotheken durchgesehen hatte, kombiniert mit Vues Datenbindung+ Debugging-Tools, habe ich eine neue Canvas-Bibliothek Easycanvas (Github-Adresse) erstellt, und wie Vue unterstützt sie ein Plug-In zum Debuggen von Elementen im Leinwand.
Auf diese Weise wird der Rendering-Teil des gesamten Spiels viel einfacher. Sie müssen nur den aktuellen Status des Spiels verwalten und die Daten basierend auf den vom Server zurückgegebenen Daten aktualisieren. Easycanvas ist für den Link „Änderungen in Daten führen zu Änderungen in der Ansicht“ verantwortlich. Bei der Implementierung der Player-Verpackungselemente im Bild unten müssen wir beispielsweise nur die Position des Verpackungsbehälters und die Anordnungsregeln für jedes Element im Rucksack angeben und dann jedes verpackte Element an ein Array binden und dann Verwalten Sie dieses Array. Ja (Easycanvas ist für die Zuordnung der Daten zum Bildschirm verantwortlich).
Zum Beispiel können die Stile von 40 Elementen in 5 Zeilen und 8 Spalten in der folgenden Form an Easycanvas übergeben werden (Index ist der Elementindex, der Abstand zwischen Elementen in die x-Richtung beträgt 36 und der Abstand in y-Richtung 32). Und diese Logik ist unveränderlich, unabhängig davon, wie sich die Anordnung der Elemente ändert oder wohin das Paket gezogen wird, die relative Position jedes Elements ist festgelegt. Beim Rendern auf Leinwand muss das Projekt selbst nicht berücksichtigt werden, sodass die Wartbarkeit besser ist.
style: { tw: 30, th: 30, tx: function () { return 40 + index % 8 * 36; }, ty: function () { return 31 + Math.floor(index / 8) * 32; } }
Canvas-Layer-Rendering
Annahme: Das Spiel muss 40 Frames beibehalten, der Browser ist 800 breit und 600 hoch, mit einer Fläche von 480.000 (im Folgenden als 480.000 als 1 Bildschirmfläche bezeichnet).
Wenn zum Rendern dieselbe Leinwand verwendet wird, beträgt die Bildnummer dieser Leinwand 40 und es müssen mindestens 40 Bildschirmbereiche pro Sekunde gezeichnet werden. Es ist jedoch wahrscheinlich, dass sich mehrere Elemente am selben Koordinatenpunkt überlappen. Beispielsweise überlappen sich die Benutzeroberfläche, die Gesundheitsleiste und die Schaltflächen unten und blockieren gemeinsam die Szenenkarte. Wenn man diese zusammenzählt, kann die Zeichenmenge des Browsers pro Sekunde leicht mehr als 100 Bildschirmbereiche erreichen.
Diese Zeichnung ist schwer zu optimieren, da die Ansicht überall auf der gesamten Leinwand aktualisiert wird: Es kann sich um die Bewegung von Spielern und Tieren handeln, es können die Spezialeffekte von Schaltflächen sein, es kann der Effekt von sein eine bestimmte Fähigkeitsänderung. In diesem Fall wird die gesamte Leinwand neu gezeichnet, selbst wenn sich der Spieler nicht bewegt, weil die Kleidung „im Wind flattert“ (eigentlich wird die Sprite-Animation mit dem nächsten Bild abgespielt) oder eine Flasche Trank darauf erscheint der Boden. Da es fast unmöglich ist, dass ein bestimmter Frame des Spiels nicht vom vorherigen Frame zu unterscheiden ist, ist es schwierig, auch nur einen Teil des Spielbildschirms unverändert zu lassen. Der gesamte Spielbildschirm wird immer aktualisiert.
Weil es fast unmöglich ist, dass ein bestimmter Frame des Spiels nicht vom vorherigen Frame zu unterscheiden ist und der Bildschirm immer aktualisiert wird.
Daher habe ich dieses Mal die überlappende Anordnung von drei Leinwänden übernommen. Da die Ereignisverarbeitung von Easycanvas die Übermittlung unterstützt, kann auch die nachfolgende Leinwand das Ereignis empfangen, selbst wenn auf die obere Leinwand geklickt wird und kein Element einen Klick beendet. Die drei Leinwände sind für die Benutzeroberfläche, den Boden (Karte) und die Elfen (Charaktere, Tiere, Fertigkeitseffekte usw.) verantwortlich:
Der Vorteil dieser Schichtung ist, dass die maximale Anzahl von Bildern pro Ebene beträgt. Kann nach Bedarf angepasst werden:
Zum Beispiel die UI-Ebene, da sich viele UIs normalerweise nicht bewegen und selbst wenn sie sich bewegen, kein allzu genaues Zeichnen erforderlich ist, sodass die Anzahl der Frames entsprechend reduziert werden kann, beispielsweise auf 20. Wenn die körperliche Stärke des Spielers auf diese Weise von 100 auf 20 sinkt, kann die Ansicht innerhalb von 50 ms aktualisiert werden, und der 50 ms-Wechsel ist für den Spieler nicht spürbar. Da es schwierig ist, Änderungen in den Daten der UI-Ebene, wie z. B. der körperlichen Stärke, mehrmals hintereinander in kurzer Zeit zu ändern, und die Verzögerung von 50 ms für Menschen schwer wahrnehmbar ist, ist kein häufiges Zeichnen erforderlich. Wenn wir 20 Bilder pro Sekunde einsparen, können wir wahrscheinlich 10 Bildschirmbereiche beim Zeichnen einsparen.
Wie der Boden ändert sich auch die Karte nur, wenn sich der Spieler bewegt. Auf diese Weise kann, wenn sich der Player nicht bewegt, 1 Bildschirmbereich pro Frame eingespart werden. Da eine reibungslose Bewegung der Spieler gewährleistet sein muss, sollte die maximale Bildrate am Boden nicht zu niedrig sein. Wenn der Bodenrahmen 30 Bilder umfasst, können 30 Bildschirmbereiche pro Sekunde gespeichert werden, wenn sich der Spieler nicht bewegt (in diesem Projekt wird die Karte fast so gezeichnet, dass sie den Bildschirm ausfüllt). Darüber hinaus wird der Boden durch die Bewegung anderer Spieler und Tiere nicht verändert und es besteht keine Notwendigkeit, die Bodenebene neu zu zeichnen.
Die maximale Bildrate der Sprite-Ebene kann nicht reduziert werden. Diese Ebene zeigt die Kernteile des Spiels, wie z. B. Charakterbewegungen, daher ist die maximale Bildrate auf 40 eingestellt.
In Auf diese Weise wird der pro Sekunde gezeichnete Bereich, Spielerbewegung Wenn sich der Spieler nicht bewegt, können es 80 bis 100 Bildschirmbereiche sein, aber nur 50 Bildschirmbereiche, wenn sich der Spieler nicht bewegt. Im Spiel halten die Spieler im Stehen an, um gegen Monster zu kämpfen, Gegenstände zu sammeln, zu organisieren und Fertigkeiten freizugeben. Daher wird das Ziehen des Bodens für eine lange Zeitspanne nicht ausgelöst, was die Leistung erheblich spart.
Serverseite
Da das Ziel darin besteht, ein Multiplayer-Onlinespiel in js zu implementieren, verwendet die Serverseite Knoten und Socket, um mit dem Browser zu kommunizieren. Ein weiterer Vorteil besteht darin, dass eine gemeinsame Logik an beiden Enden wiederverwendet werden kann, beispielsweise die Bestimmung, ob sich an einem bestimmten Koordinatenpunkt auf der Karte ein Hindernis befindet.
Spielbezogene Daten wie Spieler und Szenen auf der Node-Seite werden alle im Speicher gespeichert und regelmäßig mit Dateien synchronisiert. Bei jedem Start des Node-Dienstes werden Daten aus der Datei in den Speicher gelesen. Auf diese Weise steigt die Häufigkeit des Lesens und Schreibens von Dateien exponentiell an, wenn mehr Player vorhanden sind, was zu Leistungsproblemen führt. (Um die Stabilität zu verbessern, wurde später ein Puffer zum Lesen und Schreiben von Dateien hinzugefügt, wobei die Methode „Memory-File-Backup“ verwendet wurde, um Dateischäden durch Serverneustarts während des Lese- und Schreibvorgangs zu vermeiden.)
Die Knotenseite ist in mehrere Schichten wie Schnittstelle, Daten und Instanz unterteilt. Die „Schnittstelle“ ist für die Interaktion mit dem Browser verantwortlich. „Daten“ sind einige statische Daten, wie der Name und die Wirkung eines bestimmten Medikaments, die Geschwindigkeit und die körperliche Stärke eines bestimmten Monsters, und sind Teil der Spielregeln. „Instanz“ ist der aktuelle Zustand im Spiel. Beispielsweise ist ein Medikament bei einem bestimmten Spieler eine Instanz von „Medikamentendaten“. Ein anderes Beispiel: „Hirschinstanz“ hat das Attribut „aktuelles Blutvolumen“. Hirsch A kann 10 sein, Hirsch B kann 14 sein und „Hirsch“ selbst hat nur „anfängliches Blutvolumen“.
3. Implementierung der Szenenkarte
Kartenszene
Beginnen wir mit dem Kartenszenenteil, der zum Rendern immer noch auf Easycanvas angewiesen ist.
Denken
Da der Spieler immer in der Mitte des Bildschirms fixiert ist, entspricht die Bewegung des Spielers tatsächlich der Bewegung der Karte. Wenn der Spieler beispielsweise nach links rennt, verschiebt sich die Karte nach rechts. Wie gerade erwähnt, befindet sich der Spieler in der mittleren Ebene der drei Leinwände und die Karte gehört zur unteren Ebene, sodass der Spieler die Karte blockieren muss.
Das scheint vernünftig, aber wenn es einen Baum auf der Karte gibt, dann ist „das Level des Spielers ist immer höher als der Baum“ falsch. Derzeit gibt es zwei große Lösungen:
Kartenebenen, „Boden“ und „Übergrund“ getrennt. Platzieren Sie den Spieler zwischen zwei Ebenen, zum Beispiel im Bild unten, wobei die linke Seite auf dem Boden und die rechte Seite auf dem Boden liegt, und überlappen Sie ihn dann und zeichnen Sie, sodass der Charakter in der Mitte eingeklemmt wird:
Das sieht so aus, als ob das Problem gelöst wurde, hat aber tatsächlich zwei neue Probleme mit sich gebracht: Das erste besteht darin, dass Spieler manchmal durch Dinge „auf dem Boden“ (z. B. einen Baum) blockiert werden können, und manchmal müssen sie es auch in der Lage sein, Dinge „auf dem Boden“ zu blockieren (z. B. wenn man unter dem Baum steht, verdeckt der Kopf den Baum). Ein weiteres Problem besteht darin, dass die Leistungskosten beim Rendern steigen. Da sich die Spieler ständig ändern, muss die Ebene „Boden“ häufig neu gezeichnet werden. Dadurch wird auch das ursprüngliche Design gebrochen – um die Darstellung der großen Bodenkarte so weit wie möglich einzusparen, was die Schichtung der Leinwand komplizierter macht.
Die Karte ist nicht geschichtet, „Boden“ und „Übergrund“ werden zusammen gezeichnet. Wenn sich der Spieler hinter einem Baum befindet, stellen Sie die Transparenz des Spielers auf 0,5 ein, beispielsweise wie unten gezeigt:
Dies hat nur einen Nachteil: Der Körper des Spielers ist entweder undurchsichtig oder durchscheinend (auf der Karte laufende Monster haben diesen Effekt ebenfalls), was nicht ganz realistisch ist. Denn der ideale Effekt ist eine Szene, in der ein Teil des Körpers des Spielers verdeckt ist. Aber das ist leistungsfreundlich und der Code ist einfach zu warten. Ich verwende derzeit diese Lösung.
Wie kann man also feststellen, welche Teile des „Karten“-Bildes Bäume sind? Spiele verfügen normalerweise über eine große Kartenbeschreibungsdatei (eigentlich ein Array), die Zahlen wie 0, 1 und 2 verwendet, um zu identifizieren, welche Orte passiert werden können, wo es Hindernisse gibt, welche Orte Transferpunkte sind usw. Die „Beschreibungsdatei“ in Legend of Hot Blood wird in 48x32 als kleinster Einheit beschrieben, sodass die Aktionen des Spielers in Legend ein „Schachbrett“-Feeling haben. Je kleiner die Einheit, desto glatter ist sie, aber je größer das Volumen, das sie einnimmt, und desto zeitaufwändiger ist die Erstellung dieser Beschreibung.
Kommen wir zur Sache.
Implementierung
Ich habe einen Freund gebeten, mir beim Exportieren der Karte der „Beach Province“ in den Legend of Legend-Client zu helfen, der 33600 ist breit und 33600 hoch 22400, hundertmal so groß wie mein Computer. Um zu verhindern, dass der Computer explodiert, muss er zum Laden in mehrere Blöcke aufgeteilt werden. Da die kleinste Einheit einer Legende 48x32 ist, teilen wir die Karte in 4900 (70x70) Bilddateien mit 480x320 auf.
Wir haben die Größe der Leinwand auf 800 x 600 festgelegt, sodass Spieler nur 3 x 3, also insgesamt 9 Bilder, laden müssen, um die gesamte Leinwand abzudecken. 800/480=1,67, warum also nicht 2x2? Denn es kann sein, dass die aktuelle Position des Spielers dazu führt, dass einige Bilder nur teilweise angezeigt werden. Wie unten gezeigt:
Ich glaube, dass Sie die Methode beherrschen, nachdem Sie den Fall in diesem Artikel gelesen haben. Weitere spannende Informationen Bitte achten Sie auf andere chinesische PHP-Websites. Verwandte Artikel!
Empfohlene Lektüre:
So stellen Sie den Remote-Modus des Webpack-Dev-Servers ein
So rufen Sie den übergeordneten Server an in einer untergeordneten Komponente in ES6 Component
Das obige ist der detaillierte Inhalt vonJS-Imitation des klassischen legendären Spiels. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!