|
Multiplayer |
Top Previous Next |
|
In diesem Monat wagen wir uns an eine Echtzeit Multiplayer Anwendung. Keine rundenbasierten Spiele, Chatanwendungen usw. mehr (obwohl ich meine Welpen vom letzten Monat vermissen werde).
Falls Sie denken, dass Echtzeit Multiplayer Anwendungen kompliziert sind, dann habe ich eine Überraschung für Sie: Schauen Sie sich einfach diese Echtzeitanwendung an.
#include <acknex.h> #include <default.c>
function move_players() { while (1) { my.x += my.skill1; my.y += my.skill2; ent_sendnow(my); wait (1); } }
function main() { fps_max = 60; level_load ("multiplayer5.wmb"); camera.z = 1000; camera.tilt = -90; if (!connection) sys_exit(NULL); if (connection == 2) { my = ent_create ("red.mdl", vector (100, 50, 40), move_players); while (1) { my.skill1 = 8 * (key_w - key_s) * time_step; my.skill2 = 6 * (key_a - key_d) * time_step; send_skill (my.skill1, SEND_VEC); wait (1); } } if (connection == 3) { my = ent_create ("blue.mdl", vector (-100, -50, 40), move_players); while (1) { my.skill1 = 8 * (key_w - key_s) * time_step; my.skill2 = 6 * (key_a - key_d) * time_step; wait (1); } } }
Ja, das ist schon der gesamte Code für eine der kürzesten Echtzeitanwendungen überhaupt. Starten Sie zunächst server.bat und danach client.bat aus dem multiplayer5.cd Ordner.
Sie können jede Kugel mit dem WASD Tasten bewegen und werden dies auch in dem anderen Fenster sehen. Ich habe eine Kamera in Vogelperspektive verwendet, die auch einen einfachen Level herabschaut, der im Wesentlichen aus einem Hohlwürfel besteht:
Schauen wir uns den Code etwas genauer an:
function main() { fps_max = 60; level_load ("multiplayer5.wmb"); camera.z = 1000; camera.tilt = -90; if (!connection) sys_exit(NULL); if (connection == 2) { my = ent_create ("red.mdl", vector (100, 50, 40), move_players); while (1) { my.skill1 = 8 * (key_w - key_s) * time_step; my.skill2 = 6 * (key_a - key_d) * time_step; send_skill (my.skill1, SEND_VEC); wait (1); } } if (connection == 3) { my = ent_create ("blue.mdl", vector (-100, -50, 40), move_players); while (1) { my.skill1 = 8 * (key_w - key_s) * time_step; my.skill2 = 6 * (key_a - key_d) * time_step; wait (1); } } }
Wir begrenzen die über das Netzwerk gesendete Datenmenge auf 60 fps, laden das Level und legen die Position und Ausrichtung der Kamera fest. Falls die vordefinierte variable connection den Wert 0 hat, beenden wir das Programm; dies geschieht z. B. wenn Sie client.bat vor server.bat aufrufen.
Falls connection den Wert 2 hat, dann läuft diese Instanz des Spiels als Client; wir erstellen die rote Kugel bei den Koordinaten (100; 50; 40) und geben ihr die Funktion move_players (). Wir werden den Ball mit Hilfe seiner Skill1 und Skill2 Werte bewegen, also werden mit Hilfe des WASD Tasten positive bzw. negative Werte eingetragen. Modifizieren Sie die „8“, falls Sie eine andere Geschwindigkeit in X-Richtung wünschen und entsprechend die „6“ für die Y-Achse.
Die letzte Codezeile der ersten Schleife hat eine besondere Bedeutung: sie sendet die Werte der skill1 bis skill3 Variablen der roten Kugel an den Server.
send_skill (my.skill1, SEND_VEC);
Die „send_skill“ Anweisung sendet einen (oder mehrere) Skills an den Server, wenn sie vom Client ausgeführt wird. „SEND_VEC“ sagt der Anweisung, dass auch die folgenden beiden Skills mit gesendet werden sollen. In unserem Beispiel verwenden wir nur skill1 und skill2, Sie können (und sollten!) in einem „richtigen“ Spiel skill3 für Bewegungen in Z-Richtung verwenden, also für Sprünge, Schwerkraft etc.
Falls connecton den ‚Wert 3 hat, läuft diese Instanz des Spiels auf der Server (der zugleich Client ist). Wir erstellen die blaue Kugel und bewegen sie mit Hilfe der gleichen move_players() und den WASD Tasten. Diese Mal benötigen wir send_skill nicht, da die Bewegung auf der Server direkt durchgeführt wird.
Dies ist auch eine wichtige Regel: Die Bewegung muss vom Server kontrolliert werden. Erlauben Sie es den Clients nicht, wichtige Entities (Spieler etc) selbst zu bewegen.
Trotzdem kann ein unwichtiger NPC (Vogel etc.) auch auf dem Client bewegt werden. Allerdings sollten die Spielerbewegungen niemals direkt vom Client kontrolliert werden.
Schauen wir uns nun die zweite Funktion an, die sich darum kümmert, dass sich die Kugeln tatsächlich bewegen:
function move_players() { while (1) { my.x += my.skill1; my.y += my.skill2; ent_sendnow(my); wait (1); } }
Diese Funktion läuft sowohl auf dem Client als auch auf dem Server. Sie verändert die X- und Y-Position von den Kugeln abhängig von ihren skill1 und skill2 Werten. Die „ent_sendnow“ Anweisung überträgt die aktualisierten Positionen in jedem Frame. Ohne diese Anweisung würde die Engine diese Position seltener über das Netzwerk schicken, abhängig vom Wert der Variablen „dplay_entrate“.
Fassen wir dieses Beispiel noch einmal kurz zusammen: 1. Der Client und der Server verwenden die WASD Tasten um die skill1 Werte und skill2 Werte der Kugeln zu setzen. 2. Die Kugel auf dem Server wird direkt bewegt, wohingegen die Kugel auf dem Client eine „send_skill“ Anweisung benötigt um diese Werte dem Server zugänglich zu machen. 3. Schließlich stellt „ent_sendnow“ sicher, dass die Positionen der Kugeln in jedem Frame aktualisiert werden. Dadurch ist gewährleistet, dass die Bewegung flüssig wirkt.
Hier haben Sie es also, dass kürzeste voll funktionsfähige Beispiel einer Echtzeit-Multiplayer-Anwendung. Wie Sie sich vorstellen können, ist der Code für eine „echte“ solche Anwendung mit Kollisionserkennung, Animationen usw. viel komplizierter… oder auch nicht! Schauen Sie sich den Code unseres zweiten Beispiels an:
#include <acknex.h> #include <default.c>
function move_players() { var walk_percentage; var stand_percentage; while (1) { c_move(my, vector(my.skill1, 0, 0), nullvector, GLIDE); my.pan += my.skill2; if (my.skill1) { walk_percentage += 1.5 * my.skill1 * sign(my.skill1); ent_animate(my, "walk", walk_percentage, ANM_CYCLE); stand_percentage = 0; } else { stand_percentage += 1.2 * time_step; ent_animate(my, "stand", stand_percentage, ANM_CYCLE); walk_percentage = 0; } ent_sendnow(my); wait (1); } }
function main() { fps_max = 60; level_load ("multiplayer6.wmb"); vec_set(camera.x, vector (-600, 0, 100)); if (!connection) sys_exit(NULL); if (connection == 2) { my = ent_create ("redguard.mdl", vector (100, 50, 40), move_players); while (1) { my.skill1 = 5 * (key_w - key_s) * time_step; my.skill2 = 4 * (key_a - key_d) * time_step; send_skill (my.skill1, SEND_VEC); wait (1); } } if (connection == 3) { my = ent_create ("blueguard.mdl", vector (-100, -50, 40), move_players); while (1) { my.skill1 = 5 * (key_w - key_s) * time_step; my.skill2 = 4 * (key_a - key_d) * time_step; wait (1); } } }
Sehen Sie? Es ist überhaupt nicht kompliziert. Wenn Sie genau hinsehen, werden Sie merken, dass die Funktion main beinahe identisch ist. Wir haben eine andere Kameraposition und leicht veränderte Bewegungsgeschwindigkeiten, doch davon abgesehen hat sich nichts geändert.
Starten Sie nun server.bat und danach client.bat aus dem multiplayer6.cd Ordner. Sie sollten folgendes sehen:
Verwenden Sie wieder die WASD Tasten, um die Spieler zu bewegen. Dieses Mal bleiben sie aneinander hängen und sind animiert. Diese Neuerungen stehen in der verbesserten move_players () Funktion. Schauen wir uns diese gleich einmal an
function move_players() { var walk_percentage; var stand_percentage; while (1) { c_move(my, vector(my.skill1, 0, 0), nullvector, GLIDE); my.pan += my.skill2; if (my.skill1) { walk_percentage += 1.5 * my.skill1 * sign(my.skill1); ent_animate(my, "walk", walk_percentage, ANM_CYCLE); stand_percentage = 0; } else { stand_percentage += 1.2 * time_step; ent_animate(my, "stand", stand_percentage, ANM_CYCLE); walk_percentage = 0; } ent_sendnow(my); // send the updated entity position regardless of the "dplay_entrate" value, etc wait (1); } }
Wir verwenden skill1 der Entity um sie zu bewegen und skill2 um sie zu drehen. Falls skill1 nicht gleich 0 ist (der Spieler bewegt sich) erhöhen wir den Wert der Variablen „walk_percentage“. Stören Sie sich nicht an dem Ausdruck „sign (my.skill1)“. Er ist nur dazu da, dafür zu sorgen, dass dieser Wert immer positiv ist. Die letze Codezeile im „if“ – Teil setzt stand_percentage zurück, damit diese Animation stets von vorne beginnt.
Der „else“ – Teil vergrößert stand_percentage unabhängig von den Eingaben des Spielers. Dann animieren wie diesen und setzen wie oben walk_percentage zurück.
Schließlich sorgt die „ent_sendnow“ Anweisung dafür, dass die Position in jedem Frame aktualisiert wird und verhilft uns auf einer flüssigen Bewegung auf Server und Client. Wie Sie sehen, verwendet der Client seinen eigenen Animationscode. Es ist nicht notwendig, diese Daten auf dem Server zu berechnen und an die Clients zu schicken.
Für mich war dies ein aufregender Workshop, der Ihnen hoffentlich auch gefallen hat. Im nächsten Monat wollen wir uns weiter mit dem Thema Multiplayer-Anwendungen befassen, also bleiben Sie dran.
|