Die Entwicklung von Multiplayer-Spielen ist aus vielen Gründen kompliziert: Ihr Hosting kann teuer sein, die Struktur ist nicht offensichtlich und die Implementierung ist schwierig. In diesem Tutorial werde ich versuchen, Ihnen zu helfen, die letzte Barriere zu überwinden.
Dieser Artikel richtet sich an Entwickler, die Spiele erstellen können und mit JavaScript vertraut sind, aber noch nie Multiplayer-Online-Spiele geschrieben haben. Nach Abschluss dieses Tutorials beherrschen Sie die Implementierung grundlegender Netzwerkkomponenten in Ihrem Spiel und können sie zu etwas mehr entwickeln! Folgendes werden wir erstellen:
Sie können das fertige Spiel
hier spielen ! Wenn Sie die W- oder "Auf" -Taste drücken, nähert sich das Schiff dem Cursor. Wenn Sie mit der Maus klicken, schießt es.
(Wenn niemand online ist, öffnen Sie zwei Browserfenster auf einem Computer oder eines davon auf dem Telefon, um zu überprüfen, wie der Mehrspielermodus funktioniert.) Wenn Sie das Spiel lokal ausführen möchten, ist der vollständige Quellcode auf
GitHub verfügbar.
Bei der Erstellung des Spiels habe ich die grafischen Ressourcen aus
Kenneys Piratenpaket und dem
Phaser- Spiel-Framework verwendet. In diesem Tutorial wird Ihnen die Rolle des Netzwerkprogrammierers zugewiesen. Der Ausgangspunkt wird eine voll funktionsfähige Einzelbenutzerversion des Spiels sein. Unsere Aufgabe wird es sein, einen Server auf Node.js mit
Socket.io für den Netzwerkteil zu schreiben. Um das Tutorial nicht zu überladen, werde ich mich auf Teile konzentrieren, die sich auf Multiplayer beziehen, und Konzepte überspringen, die sich auf Phaser und Node.js beziehen.
Sie müssen nichts lokal konfigurieren, da wir dieses Spiel vollständig im Browser auf
Glitch.com erstellen werden ! Glitch ist ein großartiges Tool zum Erstellen von Webanwendungen, einschließlich Backends, Datenbanken und mehr. Es eignet sich hervorragend für Prototyping, Schulung und Zusammenarbeit, und ich freue mich sehr, Ihnen in diesem Lernprogramm die Funktionen vorstellen zu können.
Fangen wir an.
1. Vorbereitung
Ich habe den Entwurf des Projekts auf
Glitch.com veröffentlicht .
Tipps zur Benutzeroberfläche: Sie können die Anwendungsvorschau starten, indem Sie auf die Schaltfläche Anzeigen (oben links) klicken.
Die vertikale Seitenleiste links enthält alle Anwendungsdateien. Um diese Anwendung zu bearbeiten, müssen Sie ihren „Remix“ erstellen. Also werden wir eine Kopie davon in unserem Konto erstellen (oder "Gabel" im Git-Jargon). Klicken Sie auf die Schaltfläche
Diesen Remix .
Zu diesem Zeitpunkt bearbeiten Sie die Anwendung unter einem anonymen Konto. Um Ihre Arbeit zu speichern, können Sie sich anmelden (oben rechts).
Bevor Sie fortfahren, ist es wichtig, dass Sie sich mit dem Spiel vertraut machen, in dem wir einen Mehrspielermodus hinzufügen. Schauen Sie sich
index.html an . Es hat drei wichtige Funktionen, die Sie kennen müssen:
preload
(Zeile 99),
GameLoop
(Zeile 115) und
GameLoop
(Zeile 142) sowie das Player-Objekt (Zeile 35).
Wenn Sie lieber durch Üben lernen möchten, müssen Sie die Arbeit des Spiels verstehen, indem Sie die folgenden Aufgaben ausführen:
- Erhöhen Sie die Größe der Welt (Zeile 29). Beachten Sie, dass es eine separate Weltgröße für die Welt im Spiel und eine Fenstergröße für die Seitenfläche selbst gibt .
- Machen Sie es möglich, mit Hilfe des „Leerzeichens“ (Zeile 53) vorwärts zu gehen .
- Ändern Sie den Schiffstyp des Spielers (Zeile 129).
- Verlangsamen Sie die Bewegung der Muscheln (Zeile 155).
Installieren Sie Socket.io
Socket.io ist eine Bibliothek zum Verwalten der Echtzeitkommunikation in einem Browser mithilfe von
WebSockets (anstelle von Protokollen wie UDP, mit denen klassische Multiplayer-Spiele erstellt werden). Darüber hinaus verfügt die Bibliothek über redundante Möglichkeiten, um den Betrieb sicherzustellen, auch wenn WebSockets nicht unterstützt werden. Das heißt, sie befasst sich mit Messaging-Protokollen und ermöglicht die Verwendung eines praktischen ereignisbasierten Messaging-Systems.
Als erstes müssen wir das Socket.io-Modul installieren. In Glitch können Sie dazu in die Datei
package.json gehen und dann entweder das erforderliche Modul in die Abhängigkeiten eingeben oder auf
Paket hinzufügen klicken und "socket.io" eingeben.
Jetzt ist der richtige Zeitpunkt, um mit Serverprotokollen umzugehen. Klicken Sie links auf die Schaltfläche
Protokolle , um das Serverprotokoll zu öffnen. Sie sollten sehen, dass Socket.io mit all seinen Abhängigkeiten installiert wird. Hier müssen Sie nach allen Fehlern und der Ausgabe des Servercodes suchen.
Gehen wir jetzt zu
server.js . Hier befindet sich unser Servercode. Bisher gibt es nur einen grundlegenden Code für die Bereitstellung unseres HTML-Codes. Fügen Sie oben in der Datei eine Zeile hinzu, um Socket.io zu aktivieren:
var io = require('socket.io')(http);
Jetzt müssen wir auch Socket.io im Client aktivieren. Kehren wir also zu
index.html zurück und fügen die folgenden Zeilen in das
<head>
-Tag ein:
<!-- Socket.io --> <script src="/socket.io/socket.io.js"></script>
Hinweis: Socket.io verarbeitet das Laden der Clientbibliothek automatisch über diesen Pfad, sodass diese Zeile auch dann funktioniert, wenn sich in Ihren Ordnern kein Verzeichnis /socket.io/ befindet.Jetzt ist Socket.io im Projekt enthalten und einsatzbereit!
2. Erkennen und Laichen von Spielern
Unser erster wirklicher Schritt wird darin bestehen, Verbindungen auf dem Server zu akzeptieren und neue Player im Client zu erstellen.
Serververbindungen akzeptieren
Fügen Sie diesen Code am Ende von
server.js hinzu :
Daher bitten wir Socket.io, alle
connection
abzuhören, die automatisch auftreten, wenn ein Client eine Verbindung herstellt. Die Bibliothek erstellt für jeden Client ein neues
socket
Objekt, wobei
socket.id
die eindeutige Kennung für diesen Client ist.
Um zu überprüfen, ob dies funktioniert, kehren Sie zum Client (
index.html ) zurück und fügen Sie diese Zeile irgendwo in der
Erstellungsfunktion hinzu :
var socket = io();
Wenn Sie das Spiel starten und das Serverprotokoll anzeigen (klicken Sie auf die Schaltfläche
Protokolle ), sehen Sie, dass der Server dieses Verbindungsereignis registriert hat!
Wenn wir nun einen neuen Spieler verbinden, erwarten wir, dass er uns Informationen über seinen Zustand gibt. In unserem Fall müssen wir mindestens
x ,
y und
Winkel kennen , um es am richtigen Punkt korrekt zu erstellen.
Das
connection
war ein Inline-Ereignis, das von Socket.io ausgelöst wurde. Wir können alle unabhängig festgelegten Ereignisse anhören. Ich werde meinen Event
new-player
benennen und ich erwarte, dass der Client ihn sendet, sobald er sich mit Informationen über seine Position verbindet. Es wird so aussehen:
Wenn Sie diesen Code ausführen, bis Sie etwas im Serverprotokoll sehen, weil wir den Client noch nicht angewiesen haben, dieses Ereignis für
new-player
zu generieren. Aber tun wir für einen Moment so, als hätten wir dies bereits getan, und arbeiten wir weiter am Server. Was soll passieren, nachdem der Standort eines neuen Spielers gefunden wurde?
Wir können allen
anderen verbundenen Spielern eine Nachricht senden, damit sie wissen, dass ein neuer Spieler aufgetaucht ist. Socket.io hat hierfür eine praktische Funktion:
socket.broadcast.emit('create-player',state_data);
Wenn
socket.emit
aufgerufen wird
socket.emit
Nachricht einfach an diesen einzelnen Client übergeben. Wenn
socket.broadcast.emit
aufgerufen wird
socket.broadcast.emit
es an jeden mit dem Server verbundenen Client gesendet, außer an dessen Socket diese Funktion aufgerufen wurde.
Die Funktion
io.emit
sendet
io.emit
eine Nachricht an jeden mit dem Server verbundenen Client. In unserem Schema brauchen wir das nicht, denn wenn wir eine Nachricht vom Server erhalten, in der wir aufgefordert werden, unser eigenes Schiff zu erstellen, erhalten wir ein Duplikat des Sprites, da wir bereits zu Beginn des Spiels unser eigenes Schiff erstellt haben.
Hier ist ein praktischer Tipp zu den verschiedenen Arten von Messaging-Funktionen, die wir in diesem Tutorial verwenden werden.
Der Servercode sollte nun folgendermaßen aussehen:
Das heißt, jedes Mal, wenn sich ein Spieler verbindet, erwarten wir, dass er uns eine Nachricht mit Informationen über seinen Standort sendet, und wir senden diese Daten an alle anderen Spieler, damit diese sein Sprite erstellen können.
Client-Laichen
Um diesen Zyklus abzuschließen, müssen wir nun zwei Aktionen im Client ausführen:
- Generieren Sie nach der Verbindung eine Nachricht mit den Daten unseres Standorts.
- Hören Sie sich Ereignisse zum Erstellen von Spielern an und erstellen Sie an dieser Stelle einen Spieler.
Um die erste Aktion nach dem Erstellen eines Players in der
Erstellungsfunktion (ungefähr in Zeile 135) auszuführen, können wir eine Nachricht generieren, die die Standortdaten enthält, die wir senden müssen:
socket.emit('new-player',{x:player.sprite.x,y:player.sprite.y,angle:player.sprite.rotation})
Wir müssen uns nicht um die Serialisierung der gesendeten Daten kümmern. Sie können sie in jede Art von Objekt übertragen, und Socket.io wird es für uns verarbeiten.
Testen Sie den Code, bevor Sie
fortfahren . In den Serverprotokollen sollte eine ähnliche Meldung angezeigt werden:
New player has state: { x: 728.8180247836519, y: 261.9979387913289, angle: 0 }
Jetzt wissen wir, dass unser Server eine Benachrichtigung über die Verbindung eines neuen Players erhält und die Daten über seinen Standort korrekt liest!
Als nächstes möchten wir Anfragen abhören, um einen neuen Player zu erstellen. Wir können diesen Code sofort nach dem Generieren der Nachricht platzieren. Er sollte folgendermaßen aussehen:
socket.on('create-player',function(state){
Testen Sie nun den Code . Öffne zwei Fenster mit dem Spiel und stelle sicher, dass es funktioniert.
Sie sollten sehen, dass nach dem Öffnen von zwei Clients auf dem ersten Client zwei Schiffe erstellt wurden und auf dem zweiten nur eines.
Aufgabe: Können Sie herausfinden, warum es passiert ist? Oder wie können Sie das beheben? Folgen Sie Schritt für Schritt der von uns geschriebenen Client / Server-Logik und versuchen Sie, sie zu debuggen.
Ich hoffe, Sie haben versucht, es selbst herauszufinden! Folgendes passiert: Wenn der erste Spieler eine Verbindung herstellt, sendet der Server ein Ereignis zum
create-player
eines
create-player
an alle anderen Spieler, aber es gibt noch keine Spieler, die es empfangen können. Nach dem Verbinden des zweiten Spielers sendet der Server seine Nachrichten erneut, und der erste Spieler empfängt sie und erstellt das Sprite korrekt, während der zweite Spieler die Nachricht des ersten Spielers verpasst hat.
Das Problem ist, dass der zweite Spieler später eine Verbindung zum Spiel herstellt und den Status des Spiels kennen muss. Wir müssen alle neuen Verbindungsspieler darüber informieren, dass die Spieler bereits existieren (sowie andere Ereignisse, die in der Welt stattgefunden haben), damit sie sich orientieren können. Bevor wir dieses Problem lösen, habe ich eine kurze Warnung.
Warnung zur Synchronisierung des Spielstatus
Es gibt zwei Ansätze zur Implementierung der Synchronisation aller Spieler. Die erste besteht darin, eine Mindestmenge an Informationen über die Änderungen zu senden, die über das Netzwerk vorgenommen wurden. Das heißt, jedes Mal, wenn ein neuer Spieler verbunden wird, senden wir allen anderen Spielern nur Informationen über diesen neuen Spieler (und senden eine Liste aller anderen Spieler auf der Welt an diesen neuen Spieler), und nach dem Trennen der Verbindung informieren wir alle Spieler, dass dieser bestimmte Spieler die Verbindung getrennt hat.
Der zweite Ansatz besteht darin, den gesamten Status des Spiels zu vermitteln. In diesem Fall senden wir jedes Mal, wenn Sie eine Verbindung herstellen oder trennen, jedem eine vollständige Liste aller Spieler.
Der erste Ansatz ist insofern besser, als er die Menge der über das Netzwerk übertragenen Informationen minimiert. Die Implementierung kann jedoch sehr schwierig sein und es besteht die Wahrscheinlichkeit, dass die Spieler nicht synchron sind. Die zweite stellt sicher, dass die Spieler immer synchron sind, aber jede Nachricht muss mehr Daten senden.
In unserem Fall können wir all dies zu einem gemeinsamen
update
kombinieren, anstatt zu versuchen, Nachrichten zu senden, wenn ein Player verbunden ist, um es zu erstellen, und wenn die Verbindung zum Löschen getrennt wird, um es zu löschen, sowie wenn Sie sich bewegen, um seine Position zu
update
. Dieses Update-Ereignis sendet immer die Positionen jedes Spielers an alle Kunden. Dies sollte der Server tun. Die Aufgabe des Kunden besteht darin, die weltweite Einhaltung des Empfangsstatus aufrechtzuerhalten.
Um ein solches Schema zu implementieren, werde ich Folgendes tun:
- Ich werde ein Wörterbuch der Spieler führen, dessen Schlüssel ihre ID ist, und der Wert werden Daten über ihren Standort sein.
- Fügen Sie diesem Wörterbuch einen Player hinzu, wenn es verbunden ist, und senden Sie ein Aktualisierungsereignis.
- Entfernen Sie den Player aus diesem Wörterbuch, wenn er ausgeschaltet ist, und senden Sie ein Aktualisierungsereignis.
Sie können versuchen, dieses System selbst zu implementieren, da diese Schritte recht einfach sind (
mein Feature-Tipp kann hier nützlich sein). So könnte die vollständige Implementierung aussehen:
Die Client-Seite ist etwas komplizierter. Einerseits sollten wir uns jetzt nur um das
update-players
Ereignis kümmern, andererseits sollten wir in Betracht ziehen, neue Schiffe zu erstellen, wenn der Server mehr Schiffe sendet als wir wissen, oder zu löschen, wenn zu viele davon vorhanden sind.
So gehe ich mit diesem Ereignis im Client um:
Auf der Clientseite speichere ich Schiffe im Wörterbuch
other_players
, das ich gerade oben im Skript definiert habe (es wird hier nicht angezeigt). Da der Server Spielerdaten an alle Spieler sendet, muss ich eine Prüfung hinzufügen, damit der Client kein zusätzliches Sprite für sich selbst erstellt. (Wenn Sie Probleme mit der Strukturierung haben, finden Sie hier den
vollständigen Code , der sich derzeit in index.html befinden sollte.)
Testen Sie nun den Code . Sie sollten in der Lage sein, mehrere Kunden zu erstellen und die richtige Anzahl von Schiffen an den richtigen Positionen zu sehen!
3. Synchronisation der Schiffspositionen
Hier beginnt ein sehr interessanter Teil. Wir möchten Schiffspositionen auf allen Kunden synchronisieren. Dies wird die Einfachheit der Struktur zeigen, die wir im Moment erstellt haben. Wir haben bereits ein Update-Ereignis, mit dem die Standorte aller Schiffe synchronisiert werden können. Es reicht uns, Folgendes zu tun:
- Erzwingen Sie, dass der Client jedes Mal eine Nachricht generiert, wenn er an eine neue Position wechselt.
- Bringen Sie dem Server bei, diese Verschiebungsnachricht abzuhören und das Spielerdatenelement im
players
aktualisieren. - Generieren Sie ein Update-Ereignis für alle Clients.
Und das sollte reichen! Jetzt sind Sie an der Reihe, dies selbst umzusetzen.
Wenn Sie völlig verwirrt sind und einen Hinweis benötigen, schauen Sie sich das
fertige Projekt an .
Hinweis zur Minimierung der über das Netzwerk übertragenen Daten
Die einfachste Möglichkeit, dies zu implementieren, besteht darin, die Positionen aller Spieler jedes Mal zu aktualisieren, wenn ein Bewegungsereignis von
einem Spieler empfangen wird. Es ist großartig, wenn Spieler immer sofort nach dem Erscheinen die neuesten Informationen erhalten, aber die Anzahl der über das Netzwerk übertragenen Nachrichten kann leicht auf Hunderte pro Frame ansteigen. Stellen Sie sich vor, Sie haben 10 Spieler, von denen jeder in jedem Frame eine Bewegungsnachricht sendet. Der Server muss sie an alle 10 Spieler zurückleiten. Das sind schon 100 Nachrichten pro Frame!
Es ist besser, dies zu tun: Warten Sie, bis der Server alle Nachrichten von allen Spielern empfängt, und senden Sie dann allen Spielern ein umfangreiches Update mit allen Informationen. Daher reduzieren wir die Anzahl der übertragenen Nachrichten auf die Anzahl der im Spiel vorhandenen Benutzer (anstelle des Quadrats dieser Anzahl). Das Problem hierbei ist, dass alle Benutzer die gleiche Verzögerung wie der Player mit der langsamsten Verbindung haben.
Eine andere Lösung besteht darin, die Serveraktualisierungen unabhängig von der Anzahl der vom Player empfangenen Nachrichten mit einer konstanten Häufigkeit zu senden. Ein gängiger Standard ist die Aktualisierung des Servers ungefähr 30 Mal pro Sekunde.
Bei der Auswahl der Serverstruktur sollten Sie jedoch die Anzahl der in jedem Frame übertragenen Nachrichten in den frühen Phasen der Spieleentwicklung bewerten.
4. Shell-Synchronisation
Wir sind fast fertig! Der letzte wichtige Teil ist die Synchronisierung über ein Netzwerk von Shells. Wir können es genauso implementieren wie synchronisierte Player:
- Jeder Client sendet die Positionen aller seiner Shells in jedem Frame.
- Der Server leitet sie an jeden Spieler weiter.
Aber es gibt ein Problem.
Betrugsschutz
Wenn Sie alles, was der Client überträgt, als die wahren Positionen der Granaten umleiten, kann der Spieler leicht betrügen, indem er seinen Client modifiziert und gefälschte Daten an Sie überträgt, z. B. Granaten, die sich zu den Positionen der Schiffe teleportieren. Sie können dies einfach selbst überprüfen, indem Sie die Webseite herunterladen, den Code in JavaScript ändern und erneut öffnen. Und das ist nicht nur bei Browsergames ein Problem. Im Allgemeinen können wir den vom Benutzer stammenden Daten niemals vertrauen.
Um dieses Problem teilweise zu lösen, werden wir versuchen, ein anderes Schema zu verwenden:
- Der Client generiert eine Nachricht über die Schusshülle mit ihrer Position und Richtung.
- Der Server simuliert die Bewegung von Shells.
- Der Server aktualisiert die Daten jedes Clients und übergibt die Position aller Shells.
- Clients rendern Shells an Positionen, die vom Server empfangen wurden.
Somit ist der Kunde für die Position des Projektils verantwortlich, jedoch nicht für seine Geschwindigkeit und nicht für seine weitere Bewegung. Der Kunde kann die Position der Muscheln für sich selbst ändern, dies ändert jedoch nichts an dem, was andere Kunden sehen.
Um ein solches Schema zu implementieren, fügen wir beim Auslösen eine Nachrichtengenerierung hinzu. Ich werde das Sprite nicht mehr selbst erstellen, da seine Existenz und sein Standort vollständig vom Server bestimmt werden. Jetzt sieht unser neues Projektil in
index.html folgendermaßen aus:
Auch jetzt können wir das gesamte Codefragment auskommentieren und die Shells im Client aktualisieren:
Schließlich müssen wir den Client dazu bringen, auf Shell-Updates zu warten. Ich habe beschlossen, dies auf die gleiche Weise wie bei den Spielern zu implementieren, dh der Server sendet einfach ein Array aller Shell-Positionen in einem Ereignis namens
bullets-update
, und der Client erstellt oder zerstört Shells, um die Synchronisation aufrechtzuerhalten. So sieht es aus:
Und das ist alles, was im Client sein sollte. Ich gehe davon aus, dass Sie bereits wissen, wo Sie diese Codefragmente einbetten und wie Sie alles zusammensetzen können. Wenn Sie jedoch Probleme haben, können Sie immer das fertige Ergebnis anzeigen .Jetzt müssen wir in server.js Shells verfolgen und simulieren. Zuerst erstellen wir ein Array zum Verfolgen von Muscheln, ähnlich einem Array für Spieler: var bullet_array = [];
Als nächstes hören wir uns das Projektilschuss-Ereignis an:
Jetzt simulieren wir Muscheln 60 Mal pro Sekunde:
Und der letzte Schritt besteht darin, das Update-Ereignis irgendwo innerhalb dieser Funktion zu senden (aber definitiv außerhalb der for-Schleife):
Jetzt können wir endlich das Spiel testen! Wenn alles richtig gelaufen ist, sollten Sie sicherstellen, dass die Shells auf allen Clients korrekt synchronisiert sind. Die Tatsache, dass wir dies auf dem Server implementiert haben, zwang uns zu mehr Arbeit, aber es gab uns viel mehr Kontrolle. Wenn wir beispielsweise ein Ereignis erhalten, bei dem ein Projektil abgefeuert wird, können wir überprüfen, ob die Geschwindigkeit des Projektils innerhalb eines bestimmten Intervalls liegt. Wenn dies nicht der Fall ist, wissen wir, dass dieser Spieler betrügt.5. Kollision mit Muscheln
Dies ist die letzte grundlegende Mechanik, die wir implementieren. Ich hoffe, Sie sind bereits an das Verfahren zur Planung Ihrer Implementierung gewöhnt, indem Sie zuerst die Client-Implementierung vollständig abschließen und dann auf den Server wechseln (oder umgekehrt). Diese Methode ist viel weniger fehleranfällig als das Springen, wenn sie hin und her implementiert wird.Die Kollisionsprüfung ist eine wichtige Spielmechanik, daher möchten wir, dass sie vor Betrug geschützt wird. Wir implementieren es auf dem Server genauso wie bei Shells. Wir brauchen folgendes:- Überprüfen Sie, ob das Projektil nahe genug an einem Spieler auf dem Server ist.
- Generieren Sie ein Ereignis für alle Kunden, wenn ein Projektil einen Spieler trifft.
- Bringen Sie dem Kunden bei, das Trefferereignis anzuhören und das Schiff bei einem Treffer blinken zu lassen.
Sie können versuchen, diesen Teil selbst zu implementieren. Um das Schiff des Spielers bei einem Treffer flackern zu lassen, setzen Sie einfach seinen Alphakanal auf 0: player.sprite.alpha = 0;
Und es kehrt reibungslos zur vollen Deckkraft zurück (dies erfolgt im Player-Update). Für andere Spieler ist die Aktion ähnlich, aber Sie müssen den Alphakanal in der Update-Funktion mit einem ähnlichen Wert auf einen zurücksetzen: for(var id in other_players){ if(other_players[id].alpha < 1){ other_players[id].alpha += (1 - other_players[id].alpha) * 0.16; } else { other_players[id].alpha = 1; } }
Der einzige schwierige Teil kann sein, zu überprüfen, ob der Spieler seine eigenen Granaten nicht trifft (andernfalls erleidet er bei jedem Schießen Schaden).Beachten Sie, dass in diesem Schema, selbst wenn der Client versucht zu betrügen und sich weigert, die vom Server an ihn gesendete Treffermeldung zu akzeptieren, dies nur das ändert, was er auf seinem eigenen Bildschirm sieht. Alle anderen Spieler werden weiterhin sehen, dass sie den Spieler getroffen haben.6. Bewegungsglättung
Wenn Sie alle Schritte bis zu diesem Punkt abgeschlossen haben, kann ich Ihnen gratulieren. Sie haben gerade ein funktionierendes Multiplayer-Spiel erstellt! Senden Sie den Link an einen Freund und sehen Sie, wie die Magie des Online-Multiplayers Spieler zusammenbringen kann!Das Spiel ist voll funktionsfähig, aber unsere Arbeit endet nicht dort. Es gibt einige Probleme, die sich negativ auf das Gameplay auswirken können, und wir müssen uns mit ihnen befassen:- Wenn nicht jeder eine schnelle Verbindung hat, sieht die Bewegung der anderen Spieler sehr nervös aus.
- Muscheln scheinen langsam zu sein, weil sie nicht sofort abgefeuert werden. Bevor sie auf dem Bildschirm des Clients angezeigt werden, warten sie auf eine Rückmeldung vom Server.
Wir können das erste Problem lösen, indem wir unsere Schiffspositionsdaten im Client interpolieren. Wenn wir also nicht schnell genug Updates erhalten, können wir das Schiff reibungslos an den Ort bringen, an dem es sein sollte, und es nicht einfach dorthin teleportieren.Schalen erfordern eine komplexere Lösung. Wir möchten, dass der Server Granaten verarbeitet, um sich vor Betrug zu schützen, aber wir brauchen auch eine sofortige Reaktion: einen Schuss und ein fliegendes Projektil. Die beste Lösung ist ein hybrider Ansatz. Sowohl der Server als auch der Client können Shells simulieren, und der Server sendet weiterhin Aktualisierungen an die Positionen der Shells. Wenn sie nicht synchron sind, gehen wir davon aus, dass der Server richtig ist, und definieren die Position des Projektils im Client neu.Wir werden dieses Shell-System in diesem Tutorial nicht implementieren, aber es ist schön zu wissen, dass diese Methode existiert.Das Durchführen einer einfachen Interpolation von Schiffspositionen ist sehr einfach. Anstatt eine Position direkt im Aktualisierungsereignis festzulegen, wo wir zuerst neue Positionsdaten erhalten, speichern wir einfach die Zielposition:
In der Update-Funktion (auch auf der Client-Seite) umkreisen wir dann alle anderen Spieler und schieben sie in Richtung ihres Ziels:
Somit sendet uns der Server 30 Mal pro Sekunde Updates, aber wir können immer noch mit 60 fps spielen und das Spiel sieht immer noch reibungslos aus!Fazit
Wir haben viele Probleme untersucht. Lassen Sie uns sie auflisten: Wir haben gelernt, wie Sie Nachrichten zwischen dem Client und dem Server übertragen, den Status des Spiels synchronisieren und vom Server an alle Spieler senden. Dies ist der einfachste Weg, um ein Multiplayer-Online-Spiel zu implementieren.Wir haben auch gelernt, wie man das Spiel vor Betrug schützt, seine wichtigen Teile auf dem Server simuliert und Kunden über die Ergebnisse informiert. Je weniger Sie dem Kunden vertrauen, desto sicherer wird das Spiel.Schließlich haben wir gelernt, wie Verzögerungen mithilfe der Client-Interpolation überwunden werden können. Die Kompensation von Verzögerungen ist ein ernstes Thema und sehr wichtig (einige Spiele mit einer ausreichend großen Verzögerung werden einfach nicht mehr spielbar). Das Interpolieren während des Wartens auf das nächste Update vom Server ist nur eine Möglichkeit, das Problem zu reduzieren. Ein anderer sagt die nächsten Frames im Voraus voraus und korrigiert sie, wenn echte Daten vom Server empfangen werden, aber dieser Ansatz kann natürlich sehr schwierig sein.Eine völlig andere Möglichkeit, die Auswirkungen von Verzögerungen zu verringern, besteht darin, das Systemdesign dieses Problem umgehen zu lassen. Der Vorteil langsamer Schiffsumdrehungen besteht darin, dass es sich um eine einzigartige Bewegungsmechanik handelt und dass plötzliche Bewegungsänderungen verhindert werden können. Daher werden sie auch bei einer langsamen Verbindung das Gameplay nicht zerstören. Es ist sehr wichtig, die Verzögerung bei der Entwicklung der Grundelemente des Spiels zu berücksichtigen. Manchmal sind die besten Entscheidungen überhaupt keine technischen Tricks.Sie können eine weitere nützliche Glitch-Funktion verwenden, die darin besteht, dass Sie Ihr eigenes Projekt über die erweiterten Optionen in der oberen linken Ecke herunterladen oder exportieren können: