Dieses Mal bringe ich Ihnen das JS-imitierende Legend of Legend-Spiel (mit Code) Was sind die Vorsichtsmaßnahmen für das JS-imitierende Legend of Legend-Spiel? ein Blick.
Die erste Version des Spiels wurde 2014 entwickelt. Der Browser verwendet HTML+CSS+JS, der Server ASP+PHP, die Kommunikation Ajax und die Datenspeicherung 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 wenig Geschriebenes auf Canvas 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-Spiel zu implementieren
1. Es ist möglich, PC-seitige Online-Spiele mit js 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. Unter den aktuellen JS-Spielen gibt es nur wenige große Spiele 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 hast du dich für ein legendäres Spiel aus dem Jahr 2001 entschieden?
Der erste Grund sind natürlich meine Gefühle für alte Spiele; der andere, wichtigere Grund ist, dass ich entweder nicht weiß, wie man andere Spiele spielt, oder dass ich weiß, wie man sie spielt, aber nicht über die Materialien (Bilder, Ton) verfüge Effekte 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 dürfte 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
Browser
Beim Rendern von Bildschirmen wird Canvas verwendet.
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, müssen Sie die Ereignisse für Tiere und Gegenstände überwachen. Wenn Sie die Dom-Methode verwenden, müssen mehrere schwierige Probleme gelöst werden:
a. Die Reihenfolge des Renderns unterscheidet sich von der Reihenfolge der Ereignisverarbeitung (manchmal muss das Ereignis zuerst verarbeitet werden, wenn der Z-Index klein ist), und es ist eine zusätzliche Verarbeitung erforderlich. Zum Beispiel im obigen Beispiel: Wenn Sie auf Monster oder Gegenstände klicken, ist es einfach, auf Charaktere zu 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 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
platziert werden.
oder
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 von Canvas-Rendering-Logik und Projektlogik
Wenn verschiedene Rendering-Vorgänge von Canvas (z. B. drawImage, fillText usw.) werden zusammen mit dem Projektcode abgelegt, was zwangsläufig dazu führen wird, dass das Projekt später nicht mehr verwaltet werden kann. 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. „Änderungen in den Daten führen zu Änderungen in den Ansichten“ liegt in der Verantwortung von Easycanvas . 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).
Beispielsweise kann der Stil von insgesamt 40 Elementen in 5 Zeilen und 8 Spalten in der folgenden Form an Easycanvas übergeben werden (Index ist der Elementindex, der Abstand der Elemente in x-Richtung beträgt 36 und der Abstand in y-Richtung beträgt 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 und hat eine Fläche von 480.000 (im Folgenden als 480.000 als Bildschirmfläche bezeichnet).
Wenn zum Rendern dieselbe Leinwand verwendet wird, beträgt die Frame-Nummer 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 können Änderungen in den Effekten einer bestimmten Fähigkeit sein. In diesem Fall wird die gesamte Leinwand neu gezeichnet, selbst wenn sich der Spieler nicht bewegt, da die Kleidung „im Wind flattert“ (eigentlich wird die Sprite-Animation mit dem nächsten Bild abgespielt) oder eine Flasche Trank darauf erscheint der Boden. Weil Es ist fast unmöglich, dass ein bestimmter Frame des Spiels nicht vom vorherigen Frame zu unterscheiden ist. Selbst ein Teil des Spielbildschirms kann nur schwer unverändert bleiben. 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.
Deshalb 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), die Elfen (Charaktere, Tiere, Fertigkeitsspezialeffekte usw.) verantwortlich:
Der Vorteil dieser Schichtung besteht darin, dass die maximale Anzahl der Bilder pro Schicht je nach Bedarf angepasst werden kann:
Zum Beispiel die UI-Ebene, da sich viele Benutzeroberflächen normalerweise nicht bewegen und selbst wenn sie sich bewegen, keine allzu genaue Zeichnung 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. Denn Dinge wie körperliche Stärke Für UI-Layer-Daten ist es schwierig, sich in kurzer Zeit mehrmals kontinuierlich zu ändern, und die Verzögerung von 50 ms ist für Menschen schwer wahrnehmbar, sodass kein häufiges Zeichnen erforderlich ist. . Wenn wir 20 Bilder pro Sekunde einsparen, können wir wahrscheinlich 10 Bildschirmbereiche beim Zeichnen einsparen.
Ein weiteres Beispiel ist der Boden. Die Karte ändert sich 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. Auf dieser Ebene werden die Kernteile des Spiels wie z. B. Charakterbewegungen angezeigt, daher ist die maximale Bildrate auf 40 eingestellt.
Auf diese Weise kann die pro Sekunde gezeichnete Fläche 80 bis 100 Bildschirmbereiche betragen, wenn sich der Spieler bewegt, 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.
Serverseitig
Da das Ziel darin besteht, ein Multiplayer-Onlinespiel mit js zu implementieren, verwendet der Server Node und Sockets, 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 einer 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:
Die Karte ist geschichtet und der „Boden“ ist vom „oberirdischen“ getrennt. Platzieren Sie den Spieler beispielsweise zwischen zwei Ebenen. Im Bild unten liegt die linke Seite auf dem Boden und die rechte Seite auf dem Boden. Dann überlappen Sie den Charakter und zeichnen Sie ihn eingeklemmt in die Mitte:
Dies scheint das Problem zu lösen, führt jedoch tatsächlich zu zwei neuen Problemen: 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 in der Lage sein, Dinge „auf dem Boden“ zu blockieren den Boden“ (Wenn Sie beispielsweise unter diesem Baum stehen, blockiert Ihr 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, wie unten gezeigt:
Dies hat nur einen Nachteil: Der Körper des Spielers ist entweder undurchsichtig oder durchscheinend (diesen Effekt haben auch Monster, die auf der Karte laufen), 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.
Leistung
Ich habe einen Freund gebeten, mir beim Exportieren der Karte von „Beech Province“ im Legend of Legend-Client zu helfen. Sie ist 33600 breit und 22400 hoch, was dem Hundertfachen der Größe meines Computers entspricht. 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.
Ich glaube, dass Sie die Methode beherrschen, nachdem Sie den Fall in diesem Artikel gelesen haben. Weitere spannende Informationen finden Sie in anderen verwandten Artikeln auf der chinesischen PHP-Website!
Empfohlene Lektüre:
FileReader implementiert die lokale Vorschau vor dem Hochladen von Bildern
Detaillierte Erläuterung der Schritte von vue-dplayer zur Implementierung der HLS-Wiedergabe
Das obige ist der detaillierte Inhalt vonJS-Nachahmung des legendären Spiels (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!