3d für Anfänger - Kollision

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

    • 3d für Anfänger - Kollision

      Weitere 3d-Anfänger Tutorials gibt's hier:



      Vorwort: Dieses Tutorial befasst sich mit 3D Kollision, ein unverzichtbarer Bestandteil in fast jedem Spiel. Vorweg möchte ich schon sagen, dass nicht alles vorbildlich gelöst wurde und dass mein Beispiel durchaus Bugs enthält/enthalten kann. Dieses Tutorial erklärt nur die absoluten Grundlagen und soll nur einen groben Eindruck schaffen, wie 3D Kollision funktioniert.

      Theorie: Da es keine eingebauten Funktionen zur 3D Kollision gibt, muss man das ganze selbst erledigen. Dies kann man am geschicktesten mit einer Bounding Box machen. Dies ist eine Box, die nicht visuell existiert, sondern nur im Code. Wenn das Spieler Objekt später mit ihr in Berührung kommt, findet Kollision statt. Sie wird praktisch über das gelegt, was im Draw Code gezeichnet wird.



      In Grafik 1 sieht man die bounding Box wie man sich sie vorstellen kann. 2 stellt dagegen dar, wie sie einen gleichgroßen Block umschließt, d.h. es findet auch genau da Kollision statt, wo der Block gezeichnet wird. Auch komplexere Gebilde, wie z.B. ein Cylinder in Abbildung 3, lassen sich von einer bounding Box umschließen, die Kollision wäre jedoch nicht so perfekt wie es in Abb. 2 der Fall ist.

      Genung Theorie, jetzt kommt die Praxis:

      Wenn du jetzt noch nicht richtig mitgekommen bist, mach dir keine Sorgen. Die Theorie kann viel beim Verständnis helfen, ist jedoch nicht zwingend erforderlich und wird sich im Folgenden selbst erklären.

      Für dieses Tutorial benutze ich eine abgeänderte Variante des Examples meines Anfänger Tutorials. Am besten du lädst es dir gleich hier oder im Anhang runter. Überall wo sich ein "//-->" befindet, wird später Code zugefügt, du solltest es also später wieder entfernen. (Davon betroffen sind obj_block und obj_player)
      In dem Example befinden sich alle Resoucen, welche wir im Folgenden brauchen werden und Ich habe alles für dieses Tutorial angepasst. So sieht die Kamera nun schräger auf das Geschehen und sämtlicher Code zum Bewegen wurde vorerst aus dem Player Objekt entfernt. Auch das Create Event vom Camera Objekt würde entschlackt, da viele Zeile überflüssig waren und nur zum Erklären geeignet sind.

      Schritt 1: Vorbereitung

      Bevor wir nun mit den Kollisionen anfangen, müssen noch einige Vorbereitungen getroffen werden. Zuerst fügen wir zwei deklarieren wir zwei neue Variablen im Create Event von obj_player.

      GML-Quellcode

      1. z=0;
      2. height = 10;


      Auch wenn diese Variablen eindeutig scheinen, möchte ich sie lieber näher erklären, um Missverständnisse vorzubeugen:



      z ist immer die Höhe der unteren Fläche des Blocks. height dagegen ist vollkommen unabhängig von z. Sie ist die Höhe des Objekts ohne von z beeinflusst zu werden. Die absolute Höhe der oberen Fläche ist daher immer height + z .


      Zuerst erlauben wir unserem Spieler, sich zu bewegen. Dafür schreiben wir folgendes in das noch leere Step Event von obj_player.

      GML-Quellcode

      1. if (keyboard_check(vk_left)) {direction+=7;}
      2. if (keyboard_check(vk_right)) {direction-=7;}
      3. if (keyboard_check(vk_up)) {speed = 1}
      4. else if (keyboard_check(vk_down)) {speed = -1}
      5. else speed = 0;


      Drückt der Spieler die linke Pfeiltaste, erhöht sich die direction jeden step um 7. Andersrum gilt dies für die rechte Pfeiltaste. Ähnlich funktionieren die beiden folgenden Zeilen: Wenn die Pfeiltaste-oben gedrückt ist, wird dem Objekt der speed 1 zugewiesen. Sollte keine Taste gedrückt werden ist der speed = 0, das Objekt bewegt sich nicht.

      Unser Block kann sich nun bewegen, theoretisch. Wenn man das Ganze nun startet, wird man festellen, dass es nicht einfach ist, den Block in die gewünschte Richtung zu lenken. Dies liegt daran, dass ich die Rotation des Blocks entfernt habe, es dreht sich also nicht mehr in die Richtung in die er sich auch bewegt. Den genauen Grund werde ich später nocheinmal aufgreifen. Damit man jedoch vernünftig Koordinieren kann, kann man z.B. einen Pfeil auf den Block setzen, welcher die Richtung anzeigt.



      obj_player - Draw Event:

      GML-Quellcode

      1. d3d_transform_set_identity();
      2. d3d_transform_add_rotation_z(direction);
      3. d3d_transform_add_translation(x,y,z+height+1);
      4. draw_set_color(c_yellow);
      5. draw_set_blend_mode(bm_add);
      6. d3d_draw_floor(-3,-3,0,+3,+3,0,sprite_get_texture(spr_arrow,-1),1,1);
      7. draw_set_blend_mode(bm_normal);
      8. draw_set_color(c_white);
      9. d3d_transform_set_identity();


      Wie Rotationen funktionieren solltest du bereits wissen. Durch "d3d_draw_floor" lassen wir die Fläche zeichnen, auf der unser Pfeil angezeigt wird.
      Diesen habe ich in spr_arrow bereits vorbereitet. Sachen wie Farbe oder Blendmode sind natürlich optional.
      Wir sollten nun einen Pfeil haben, welcher sich einen Pixel über dem Block befindet und sich mit der direction des Objekts dreht.

      Im Code sollte noch die Zeile

      GML-Quellcode

      1. d3d_draw_block(x-5,y-5,0,x+5,y+5,10,tex,1,1);


      stehen. Hier müssen wir die Z Koordinaten abändern.

      GML-Quellcode

      1. d3d_draw_block(x-5,y-5,z,x+5,y+5,z+height,tex,1,1);


      Der Block wird nun auf z und z+height gezeichnet, d.h. die Höhe unseres Blocks ist nun variabel, wenn wir z ändern.

      Die Vorbeireitungen sind damit abgeschlossen, wir können nun endlich mit Kollision anfangen. Das derzeitige Resultat gibt's hier oder im Anhang.

      Schritt 2: 2D Kollision

      Da wir nichts überstürzen wollen, fangen wir klein an, mit 2D Kollision. Die Z-Achse lassen wir erstmal außen vor.
      Das erste was wir machen müssen ist, obj_player und obj_block das spr_collision zuweisen, welches sich bereits im Example befindet.
      Dies wird vorerst unsere "zweidimensionale bounding box", oder anders: Eine Bounding Box mit unendlicher Höhe.
      Für die 2D Kollision brauchen wir nun obj_block.

      In das Kollisions Event mit obj_player schreiben wir:

      GML-Quellcode

      1. with(other)
      2. {
      3. x=xprevious;
      4. y=yprevious;
      5. }


      Das "with(other)" bewirkt, dass der folgende Code so ausgeführt wird, als stände er in obj_player.
      xprevious ist die vorherige x Koordinate des Spielers. x = xprevious bewirkt also, dass obj_player nicht mehr durch den Block durchlaufen kann.
      Kollidiert man nun mit einem Block, kann man nicht an ihm "entlanggleiten", man bleibt also stecken. Um dies zu verhindern, kommt folgender Code unter die Zeile "y=yprevious;":

      GML-Quellcode

      1. if(!place_meeting(x+hspeed,y,other))
      2. {
      3. x+=hspeed;
      4. }
      5. if(!place_meeting(x,y+vspeed,other))
      6. {
      7. y+=vspeed;
      8. }




      In der ersten Zeile wir überprüft, ob keine Kollision mit other, in diesem Fall ist es obj_block, besteht. x wird solange um hspeed erhöht, bis durch
      "place_meeting(x+hspeed, [...]" ausgegeben wird, dass im nächsten step Kollision stattfindet.



      Gleiches gilt für den folgenden Code, welcher für die 2 anderen Richtungen verwantwortlich ist.

      Schritt 3: 3D Kollision

      Jetzt wo wir bereits 2D Kollision haben, kommt nun noch die Z Achse dazu. Auch dafür sind wieder ein paar Vorbereitungen zu treffen:
      In obj_player fügen wir wieder eine Variable, diesmal z_floor, hinzu.

      GML-Quellcode

      1. z_floor=0;


      Mit dieser Variable können wir später ermitteln, ob sich unter obj_player ein Block befindet und bekommen gegebenfalls seinen z+height Wert zurückgegeben.

      Im step Event ergänzen wir folgende Zeilen:

      GML-Quellcode

      1. if (keyboard_check(ord('Y'))){z+=1}
      2. if (keyboard_check(ord('X'))&& z >z_floor){z-=1}


      Mit der ersten Zeile erhöhen wir den z-Wert beim Drücken der Taste Y um 1.
      Mit X verringern wir diesen um 1, unter der Vorraussetzung, dass z größer als z_floor ist.
      Zur Erinnerung: z_floor haben wir gerade als 0 deklariert, momentan kann der Block also nicht tiefer als der Boden sein.

      Nun begeben wir uns wieder in das Kollisions Event von obj_block und ergänzen im bestehenden Code folgende Abfrage:

      GML-Quellcode

      1. if(other.z < height)
      2. {
      3. }


      Die Abfrage lässt sich durch folgende Grafik erklären.



      Kollision findet also nur statt, wenn der z-Wert von obj_player kleiner als die Höhe des Blocks ist.

      Mit obj_block sind wir soweit fertig, weiter gehts wieder bei obj_player:
      Ein Problem welches jetzt noch besteht ist, dass der Spieler von oben in einen Block sacken kann. Dafür ergänzen wir unseren Code im step Event:

      GML-Quellcode

      1. obj=instance_place(x,y,obj_block);
      2. if(obj)
      3. {
      4. z_floor = obj.height;
      5. }
      6. else
      7. {
      8. z_floor = 0;
      9. }
      10. if(z < z_floor) { z = z_floor}
      Alles anzeigen


      Die Funktion "instance_place()" ersetzt das Kollisions Event. Der Rückgabewert ist die ID vom jeweiligen obj_block, welche wir in die Variable "obj" speichern.
      Danach wird abgefragt, ob eine Kollision stattfindet. Ist der Variable obj nämlich eine ID zugewiesen, ist der Wert positiv, also true. In diesem Fall wird die Variable z_floor mit height des anderen Objekts gleichgesetzt.

      Zur Erinnerung: Folgende Zeile verhindert, dass z kleiner als z_floor werden kann:

      GML-Quellcode

      1. if (keyboard_check(ord('X'))&& z >z_floor){z-=1}


      Die letzte Zeile sorgt für Sicherheit. Sollte es passieren, dass z kleiner als z_floor ist, wird dies sofort berichtigt.

      Schritt 4: Fertigstellung


      Beim testen wird euch sicher aufgefallen sein, dass euer Spieler manchmal direkt in einem Block startet. Um dies zum Schluss noch zu verhindern, können wir folgendes noch im create Event von obj_block ergänzen:

      GML-Quellcode

      1. if(place_meeting(x,y,obj_player))
      2. {
      3. instance_destroy();
      4. }


      Damit haben wir nun eine grundlegende 3D Kollision, auch wenn sie noch viele Ecken und Kanten hat. Wenn ihr das Muster verstanden habt, wird es euch nicht schwer fallen die Idee zu erweitern. Das Resultat gibt's hier oder im Anhang.
      Dateien
      • kollision_0.zip

        (216,04 kB, 285 mal heruntergeladen, zuletzt: )
      • kollision_1.zip

        (216,24 kB, 288 mal heruntergeladen, zuletzt: )
      • kollision_2.zip

        (216,47 kB, 429 mal heruntergeladen, zuletzt: )
    • Gutes Tutorial :)

      Hab nur einen kleinen bug entdeckt.
      Wenn man an blocks entlang schleift passiert es relativ oft das man plötzlich auf dem Block landet.
      Schätze das liegt an dieser Zeile: if(z < z_floor) { z = z_floor}.
      Das Beispiel wäre noch ein bisschen besser wenn der Block springen könnte (anstelle von nur rauf und runter).
      Das tut zeigt aber gut wie 3d Kollisionen funktionieren. :thumbup:

      EDIT: hab hier einen kurzen code gemacht um Sprünge zu ermöglichen.
      Man muss es einfach nur ins Step Event des Players tun.

      if z >= z_floor
      {
      z += zspeed;
      if z -z_grav > 0 then z -= z_grav else z = z_floor
      if zspeed > 0 zspeed -= z_fric;
      }

      if (keyboard_check(vk_space)) {zspeed = 8;}

      Dieser Teil kommt ins create event. Die werte von z_fric und z_grav kann man beliebig verändern:

      z_fric = 0.5;
      zspeed = 0;
      z_grav = 2;

      Willst du auf diese Drachen und -eier klicken?
      Sie werden sich freuen ;)

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von DragonGamer ()

    • Deinen Bug konnte ich nicht reproduzieren, auch beim Erstellen des Tutorials ist mir nie ähnliches aufgefallen.
      Zum Springen: Ich versuche meine Tutorials möglichst knapp zu halten und verzichtbare Sachen gleich wegzulassen.
      Damit haben wir nun eine grundlegende 3D Kollision, auch wenn sie noch viele Ecken und Kanten hat. Wenn ihr das Muster verstanden habt, wird es euch nicht schwer fallen die Idee zu erweitern.

      Wie du selbst gezeigt hast, fällt es einem überhaupt nicht schwer, das Example zu erweitern, z.B. um das Springen. Warum also auch in das Tutorial packen?
      Ansonsten freu ich mich dass dir das Tutorial gefällt. ^^
    • Doch, der Bug ist da. Du must einen Ort suchen wo 2 Blöcke genau nebeneinander sind wobei der eine ein wenig verschoben ist.

      Schleife an einem davon Entlang und wenn du dann den zweiten Block berührst, springt man sofort auf die höhe von diesem drauf.



      Mit der Sache zum Springen hast du recht, aber es sind grad mal 8 Zeilen und es wäre, wenn man das Example versucht, einfacher mit einer Taste zu springen statt mit 2 rauf und runter zu fahren.

      Naja, egal xD

      Willst du auf diese Drachen und -eier klicken?
      Sie werden sich freuen ;)
    • Bitte Fragen, die nicht direkt etwas mit dem Tutorial zu tun haben, im Technische-Fragen Forum stellen.

      Egal ob 2D oder 3D, an Variableninitialisierungen sollte es nicht scheitern, les dich bitte vorher ein.
    • Zwar schon sehr alt aber ich glaube, das kannte ich hier noch garnicht.
      Wie immer schön geschrieben, finds toll, dass du dran geblieben bist ;)
      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
    • Benutzer online 1

      1 Besucher