39Dll: Ein simples Spiel

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • 39Dll: Ein simples Spiel

      Notiz: Das hier ist mein erstes Tutorial, ich hoffe es hilft jemandem und ich drück mich nicht zu undeutlich aus. ;)

      Nachdem ihr im Tutorial flogys Tutorial gelernt habt, wie man mithilfe der 39Dll eine Verbindung aufbaut, werde ich euch hier zeigen wie ihr ein einfaches Spiel gestalten könnt.

      Die Spielidee ist, wie der Titel schon sagt, einfach: Es geht um 2 Spieler, die jeweils einen "Flugpanzer" haben der möglichst schießen können soll. Beginnen wir bei der Umsetzung erstmal mit der grundlegenden Steuerung des "Flugpanzers":
      Dieser Panzer besteht aus einem Grundsprite, einem Geschützturm-Sprite und einem Schatten.

      obj_unit_self


      GML-Quellcode

      1. friction=0.5;



      GML-Quellcode

      1. // Geschützturm zur Maus ausrichten
      2. image_angle=point_direction(x,y,mouse_x,mouse_y)-90;
      3. //Die Steuerung selbst
      4. if keyboard_check(ord("W")){motion_add(direction,1);}
      5. if keyboard_check(ord("S")){speed-=1;}
      6. if keyboard_check(ord("A")){direction+=5;}
      7. if keyboard_check(ord("D")){direction-=5;}
      8. // Geschwindigkeitsbegrenzung
      9. if speed>4 {speed=4;}
      10. if speed<0 {speed=0;}
      Alles anzeigen



      GML-Quellcode

      1. draw_sprite_ext(sprite_index,1,x,y,0.75+speed/16,0.75+speed/16,direction-90,-1,1);
      2. draw_sprite_ext(spr_unit_shadow,1,x+0.75+speed,y+0.75+speed,0.75,0.75,direction-90,-1,0.15+speed/8);
      3. draw_sprite_ext(spr_unit_turret,1,x,y,0.75+speed/16,0.75+speed/16,image_angle,-1,1);


      Dieses Objekt können wir nun in den Raum ro_battle setzen und testen.

      Da dies wohl keine große Herausforderung darstellt, machen wir gleich mit dem Connect-Teil weiter.

      obj_connect


      GML-Quellcode

      1. //Die Erklärung zum Connect Teil findet ihr in flogys Tutorial
      2. global.listen=0;
      3. global.wait=false;
      4. global.player=0;
      5. global.socket=-10;
      6. omenu_draw="Press 'C' to create or 'J' to join a session";



      GML-Quellcode

      1. if (global.wait==true) {
      2. global.socket=tcpaccept(global.listen, true);
      3. if(global.socket < 0){
      4. exit;
      5. }
      6. setnagle(global.socket,true);
      7. room_goto(ro_battle);
      8. }
      9. if (keyboard_check(ord("J"))) {
      10. if (global.wait==false) {
      11. global.player=1;
      12. global.wait=3;
      13. omenu_draw="Trying to join a session...";
      14. screen_redraw();
      15. dllinit(0,1,1);
      16. global.socket = tcpconnect(get_string("IP des Servers",""),555, true);
      17. if global.socket>-1 {
      18. omenu_draw="Joining a session...";
      19. screen_redraw();
      20. setnagle(global.socket,true);
      21. room_goto(ro_battle);
      22. } else {
      23. omenu_draw="Error - could not join the session. Press escape to exit.";
      24. screen_redraw();
      25. }
      26. }
      27. }
      28. if (keyboard_check(ord("C"))) {
      29. if (global.wait==false) {
      30. global.player=0;
      31. omenu_draw="Creating session...";
      32. global.wait=true;
      33. screen_redraw();
      34. dllinit(0,1,1);
      35. global.listen=tcplisten(555,10,1);
      36. omenu_draw="Waiting for other players to join...";
      37. }
      38. }
      Alles anzeigen



      GML-Quellcode

      1. draw_set_font(fo_menu);
      2. draw_set_halign(fa_center);
      3. draw_set_valign(fa_middle);
      4. draw_set_color(c_white);
      5. draw_text(320,240,omenu_draw);


      Dieses Objekt setzen wir in den Raum ro_connect.

      So, jetzt haben wir einen beweglichen Flugpanzer und können schonmal connecten. Jetzt fehlt nurnoch die Übertragung der Daten. Dazu bearbeiten wir erst einmal das Objekt obj_unit_self um die Daten zu senden. Danach werden wir den Empfangsteil programmieren.

      Das Grundprinzip ist folgendes: Wenn sich der Spieler gerade bewegt, oder eine Kugel abgefeuert wird, werden (fast) jeden Step alle Daten übertragen.
      obj_unit_self


      GML-Quellcode

      1. xprev=x;yprev=y; // Die Prüfvariablen werden gesetzt



      GML-Quellcode

      1. // In diesem Skript senden wir die Koordinaten des eigenen Spielers
      2. if (!(x==xprev)||!(y==yprev)) { // Wenn sich der Spieler bewegt hat...
      3. i+=1
      4. if (i!=10) { // Alle 10 Frames wird das senden ausgelassen
      5. clearbuffer(); //...löschen wir den Puffer...
      6. writebyte(1); //...schreiben der "Switch" Variable...
      7. writeshort(x); // und sämtliche anderen Variablen in Die Nachricht,
      8. writeshort(y);
      9. writeshort(image_angle);
      10. writeshort(direction);
      11. writeshort(speed);
      12. sendmessage(global.socket); // und schicken diese dann schließlich ab.
      13. xprev=x;yprev=y; // Hier werden die Prüfvariablen wieder gesetzt
      14. }
      15. }
      16. if (i=10) {
      17. i=0;
      18. }
      Alles anzeigen


      Jetzt haben wir schonmal die Koordinaten übertragen, fehlt noch das schießen:


      GML-Quellcode

      1. if (mouse_check_button_pressed(mb_left)) {
      2. // Wenn die linke Maustaste gedrückt wurde, erstellen wir eine Kugel und schicken das an den anderen Spieler
      3. o=instance_create(x+lengthdir_x(8,image_angle+90),y+lengthdir_y(8,image_angle+90),obj_rocket); // Hier wird die Kugel erstellt
      4. o.speed=7;
      5. o.direction=image_angle+90;
      6. o.image_angle=image_angle;
      7. o.image_xscale=0.75+speed/16;
      8. o.image_yscale=0.75+speed/16;
      9. scr_send_static(); // Hier senden wir nochmal Position und den aktuellen Stand des Geschützturms
      10. clearbuffer();
      11. writebyte(2); // Und hier schließlich, das eine Kugel abgefeuert wurde
      12. sendmessage(global.socket);
      13. }
      Alles anzeigen


      Jetzt werden alle relevanten Informationen übertragen. Diese müssen wir jetzt nurnoch empfangen und verarbeiten, und es ist vollbracht!

      Dazu erstellen wir erstmal ein Gegnerobjekt,
      obj_unit_other


      GML-Quellcode

      1. friction=0.5;




      GML-Quellcode

      1. var recieve;
      2. recieve=receivemessage(global.socket);
      3. if (recieve>0) {
      4. switcher=readbyte();
      5. switch switcher {
      6. case 1:
      7. // Empfangen von Position, Ausrichtung des Geschützturms, Geschwindigkeit, Richtung
      8. x=readshort();
      9. y=readshort();
      10. image_angle=readshort();
      11. direction=readshort();
      12. speed=readshort();
      13. break;
      14. case 2:
      15. // Eine Kanonenkugel wurde abgefeuert
      16. var i;
      17. i=instance_create(x+lengthdir_x(8,image_angle+90),y+lengthdir_y(8,image_angle+90),obj_rocket);
      18. i.speed=7;
      19. i.direction=image_angle+90;
      20. i.image_angle=image_angle;
      21. i.image_xscale=0.75+speed/16;
      22. i.image_yscale=0.75+speed/16;
      23. break;
      24. }
      25. }
      26. if (recieve=0) {
      27. // Der Gegner hat das Spiel verlassen
      28. show_message("Your opponent has left");
      29. game_end();
      30. }
      Alles anzeigen


      Gezeichnet wird der Gegner genau wie der eigene, da wir hier die gleichen Variablen zur Verfügung haben.


      GML-Quellcode

      1. draw_sprite_ext(sprite_index,1,x,y,0.75+speed/16,0.75+speed/16,direction-90,-1,1);
      2. draw_sprite_ext(spr_unit_shadow,1,x+0.75+speed,y+0.75+speed,0.75,0.75,direction-90,-1,0.15+speed/8);
      3. draw_sprite_ext(spr_unit_turret,1,x,y,0.75+speed/16,0.75+speed/16,image_angle,-1,1);


      Um jetzt ein richtiges Spiel zu erstellen, fehlen natürlich noch einige Sachen. Z.B. sowas wie Lebenspunkte oder so.

      Im Example habe ich noch ein paar Sachen eingefügt, downloaden könnt ihr es hier.

      Im Example befinden sich auch alle Ressourcen.
    • Per Netzwerk dürfte das gut laufen...für ne online version würd ich ein anderes system zum simulieren der bewegung nehmen.

      Bei nem room speed von 30 sind es immer noch 3 messages pro sekunde + evtl alles was nicht nur bewegung ist. Ohne ne gute Verbindung wirds sicher nich so schön synchron.
    • Es sind 27 Messages pro Sekunde. Und ja, übers Internet wirds wohl eher eine ruckelige Angelegenheit. ;)

      Allerdings ist so eine Topdown-Engine immer relativ schwer zu lösen... komplett synchron ist praktisch unmöglich. Und für ein Tutorial das nur die "Grundroutinen" beibringen soll, ist dieses System denke ich ausreichend.
    • @ghostrider:
      Das Tutorial gefällt mir gut, da bekomm ich irgendwie lust nochmal was mit der 39dll zu machen ;)

      @Blaxun:
      Da es ja heißt man soll nie zu viele Messages verschicken, tut sich da bei mir folgendes Problem auf:
      Wenn ich nun bei meinem Spiel zum beispiel mit der Maus ständig image_angle von meiner Spielfigur ändere, dann muß ich ja, damit es einigermaßen flüßig bei dem anderen Spieler ankommt, ständig (fast jeden Step) den Wert image_angle übertragen, das kann ich ja aber nicht sonst gibts Probleme. Bei anderen (kommerziellen) Spielen funktioniert das aber doch auch irgendwie. Ist der GameMaker(+39dll) nicht dafür geeignet so schnell viele Daten zu verschicken oder würde das Problem auch bei einer anderen Programmiersprache auftreten?
    • Man sieht doch auch, was passiert, wenn es bei kommerziellen Spielen laggt : Der Player vom Mitspieler läuft plötzlich stumpf gradeaus, das Monster, das dich grad' noch verfolgt hat, läuft an dir vorbei durch 'ne Wand. Und wenn der lag dann aufhört, sind sie alle wieder an der alten Stelle und kämpfen ( z.B. ) wieder miteinander.

      Ich glaube eher, das 10-15 messages per Step mit Speed und Direction gesendet werden, jede Sekunde dann halt mal die x-y-z dazu.
    • Ich denke eher, das die momentan gedrückten Tasten gesendet werden, halt immer wenn sie geändert werden, und daraus die Bewegung errechnet wird. Sowas hatte ich auch mal ausprobiert, funktioniert eher ungenau... deswegen auch dieses x-y-z senden pro Sekunde - allerdings ruckeln dann alle Spieler alle Sekunde ein wenig.
    • GrinchXL schrieb:

      @ghostrider:
      Das Tutorial gefällt mir gut, da bekomm ich irgendwie lust nochmal was mit der 39dll zu machen ;)

      @Blaxun:
      Da es ja heißt man soll nie zu viele Messages verschicken, tut sich da bei mir folgendes Problem auf:
      Wenn ich nun bei meinem Spiel zum beispiel mit der Maus ständig image_angle von meiner Spielfigur ändere, dann muß ich ja, damit es einigermaßen flüßig bei dem anderen Spieler ankommt, ständig (fast jeden Step) den Wert image_angle übertragen, das kann ich ja aber nicht sonst gibts Probleme. Bei anderen (kommerziellen) Spielen funktioniert das aber doch auch irgendwie. Ist der GameMaker(+39dll) nicht dafür geeignet so schnell viele Daten zu verschicken oder würde das Problem auch bei einer anderen Programmiersprache auftreten?
      Du übermittelst die "Drehgeschwindigkeit" und wenn es stehen bleibt sendest du wieder die Drehgeschwindigkeit und gleichst ab, welchen Winkel es hat... damit is es relativ Synchron und kostet gesammt 3 Messages ;)
      So far, Schattenphoenix~
      _____________________________________________________________________________
      "Who needs a stairway to heaven...
      If there is an elevator to hell... ?
      "
      - Vergessen
      "Auch ein perfektes Chaos ist etwas vollkommenes."
      - Jean Genet
    • Wenn man also mit der Maus zielt, ist die Drehgeschwindigkeit immer gleich? Aha, gut zu wissen.

      Das ist Quatsch, man muss (in den meisten Fällen) den Winkel übertragen. Allerdings kann man sich das oft sparen, und nur beim Schießen /alle paar Sekunden senden. Dann halt noch ein wenig "smoothen", und es geht.

      Achso: synchroner wird es durch deine Methode auch nicht.
    • @Phoenix
      Ja, also bewegungen die über Tastendruck übermittelt werden sind ja eher kein Problem, man muß eben einfach dem anderen Spieler mit einer Message sagen das er nun die Taste zum hoch laufen gedrückt hat, wenn er die Taste los lässt das auch übermitteln + vieleicht noch die aktuellen x, y Koordinaten einmal. Die Methode ist sehr Message sparent und um einiges besser als ständig die x, y Koordinaten zu übertragen, eine kleine verzögerung wirt man da logischerweise aber auch haben. Mir gings bei meiner Frage um die drehung der Spielfigur mit der Maus, da die ja sozusagen Stufenlos ist, oder die Bewegung eines nicht Spielerobjektes, ein Gegner zum Beispiel, da seine Bewegung ja auch nicht vorhersehbar ist.

      @Schattenphoenix:
      Wenn ich jetzt die Maus ganz schnell um 180´ drehe ists halt ein bischen schlecht und wie ghostrider gesagt hat ist die Geschwindigkeit der Maus ja nie gleich. Und eigentlich hält man die Maus ja nie wirklich still bei nem Topdownshooter, meiner Meinung nach kann man dann den Winkel nur mit viel Laggs übertragen um die Verbindung nicht zu stark zu belasten. Oder hast du deine Methode mal ausprobiert?

      @ghostrider:
      Genau, das ist wohl die einzige Lösung, die Lags muß man eben in kauf nehmen.

      Also allgemein kann ich noch sagen das so schnelle Topdownshooter übers Internet meiner Meinung nach schon relativ schwer zu realisieren sind, nicht unmöglich aber schwer;). Was ich mir einigermaßen gut zu realisieren vorstellen kann, ist zum Beispiel ein Point&Click Adventure, eben so ähnlich wie Diablo. Oder halt allgemein Spiele bei denen es nicht auf Präzision und hohe Bewegunsgeschwindigkeit ankommt. Wobei mich gerade so ein Online Coop Topdownshooter total reizen würde.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von GrinchXL ()

    • Du weißt hoffentlich schon, das TCP dafür sorgt, das alle Pakete wirklich ankommen?

      EDIT:
      Ich hab mal gehört das über TCP/IP die Daten immer zu 100% ankommen, durch Lags jedoch nur verzögert, eine Prüfung wäre dadurch überflüssig.


      Ja, das liegt daran, das TCP diese Prüfung bereits intern hat. Soweit ich weiß werden da die Pakete durchnummeriert und einfach geguckt, ob was fehlt oder nicht.
    • Das schon, aber die ganzen Tutorials usw. sind alle auf TCP/IP aufgebaut, deswegen bin ich jetzt einfach mal davon ausgegangen. ;)

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von GrinchXL ()

    • Phoenix schrieb:

      Ich meint' eigentlich UDP, was - soweit ich weiß - schneller aber nicht so sicher ist.
      Die 39DLL unterstützt meines wissens auch UPD-Connections.


      Ja klar ist es schneller, weil es die ganzen Kontrollen usw. nicht mitschicken muss. Allerdings rate zumindest ich generell von UDP ab, denn es gibt immer Dinge, die man 100% sicher schicken muss. GERADE bei einem Poker/oder anderem Kartenspiel, bei dem man nicht viel senden muss, bei dem aber alles wirklich ankommen muss, ist es mächtig blöd UDP zu benutzen.
    • UDP ist schneller aber es gibt keine Garantie dass die Nachricht ankommt.
      TCP ist "langsamer" aber garantiert dass die Nachricht auch ankommt.

      Je nach Spiel/Projekt kann man natürlich UDP und TCP nutzen.
      Mann muss halt abwägen wobei UDP sinvoll ist.

      Die Sache mit dem Figuren Drehen ...naja...wie wichtig ist es denn zu wissen wohin der andere guckt?
      Hab mir das ganze nich angesehn.

      Ich habe bisher "Unwichtiges" einfach immer ausgelassen.

      Das Problem mit Game Maker ist halt dass immer nur soviele Scripte durchgegangen werden können wie Room Speed gesetzt ist.
      Wenn wir die Nachrichten im STEP event bearbeiten und Room SPeed bei 30 liegt dann können wir das nur 30 mal abarbeiten.
      Wenn es schon 27 Nachrichten sind in der Sekunde dann können noch 3 dazu...und dann ist aber schon ende.

      Wenn dann noch ein Spieler dazukommen würde würde sich alles verzögern da der client auf Grund der Einschränkung von Game Maker nicht mit der Anzahl an Nachrichten klarkommen würde.
    • ghostrider schrieb:

      Phoenix schrieb:

      Ich meint' eigentlich UDP, was - soweit ich weiß - schneller aber nicht so sicher ist.
      Die 39DLL unterstützt meines wissens auch UPD-Connections.


      Ja klar ist es schneller, weil es die ganzen Kontrollen usw. nicht mitschicken muss. Allerdings rate zumindest ich generell von UDP ab, denn es gibt immer Dinge, die man 100% sicher schicken muss. GERADE bei einem Poker/oder anderem Kartenspiel, bei dem man nicht viel senden muss, bei dem aber alles wirklich ankommen muss, ist es mächtig blöd UDP zu benutzen.


      Hast du auch wieder recht.
      Aber für unser TDS ( oder was auch immer^^ ) ist es ja auch nicht wichtig, dass 100% aller Daten ( beim Drehen ) ankommen - 2 Steps später wurde die Position eh wieder geändert.

      Außerdem könnte der Spieler, der die Daten sendet, gucken, ob sich etwas verändert hat. Wenn nicht, dann wird halt nicht gesendet, soweit ich weiß sind 2 - 3 Zeilen Code immernoch schneller als 'ne Message zu allen anderen ( oder dem Server, der schickt sie dann ja auch allen anderen ). Schwer sowas zu realisieren ist es wirklich nicht, und ich sage ganz ehrlich, die, die das nicht lösen können, sollten ( noch ) nicht aus ( 39 )DLL - Tuts zu lernen --> Tuts sind zum lernen da, ( Hollerie und Hallera :D ) und wenn man das nicht schafft, sollte man sich erstmal vielleicht mit Logik und/oder den GML Tutorials befassen.

      Das klingt jetzt ziemlich scharf und blutrünstig ( oder wie ihr es bezeichnet^^ ), heißt aber nicht, das ich nicht jedem die Hand reichen würde, der Ernsthaft sowas zu verstehen vesucht ;)

      Meine Meinung, Phoenix^^
    • Nö, seh ich genauso.

      Ablauflogiken sind verdammt wichtig.
      Ohne die Logiken läuft gar nichts.

      Direkt n PAP oder Struktogramm is evtl übertrieben....aber ohne Logik wirds einfach nix...zumindest nich bei größeren Projekten =)