Platformer-Problem bei der Überwindung eines 16x16 Abgrundes

    • GM 8

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

    • Platformer-Problem bei der Überwindung eines 16x16 Abgrundes

      Servus Community,
      Ich habe für eines meiner derzeitigen Projekte, nämlich ein Platformer, folgendes Problem.
      Wenn ich mich über einen Abgrund in der gleichen größe wie der der Spielers bewege (16x16), dass der Player entweder direkt über dieses Loch rüber läuft und es einfach ignoriert, oder das er sich festbuggt und nicht mehr weiter läuft. Damit ihr direkt wisst was ich meine habe ich HIER nochmal alle benötigten Daten samt .exe bereitgestellt!

      Hier ist der Movement-Code des Players :

      GML-Quellcode

      1. image_speed = global.PlayerImageSpeed
      2. //Überprüft ob der Player Fällt oder grade Springtif vspeed < 0 { global.PlayerJumping = 1 global.PlayerFalling = 0 }
      3. if vspeed > 0 { global.PlayerJumping = 0 global.PlayerFalling = 1 }
      4. if speed = 0 { global.PlayerJumping = 0 global.PlayerFalling = 0 }
      5. //Ist der Player in der Luft?if place_free(x,y + 1) { gravity = 0.5 }else { gravity = 0.0 }
      6. if keyboard_check(ord('A')) and !keyboard_check(ord('D')) { if place_free(x - global.PlayerMoveSpeed,y) { //Der Spieler hält also A und nicht D gedrückt. global.PlayerIdleLeft = 0 global.PlayerIdleRight = 0 global.PlayerMoveLeft = 1 //Der Player rennt nach Links. global.PlayerMoveRight = 0 x -= global.PlayerMoveSpeed //Bewegt den Spieler in die angegebene Richtung. } else { global.PlayerIdleLeft = 1 //Links ist eine Wand? Bleib stehen und guck nach Links. global.PlayerIdleRight = 0 global.PlayerMoveLeft = 0 global.PlayerMoveRight = 0 } } if keyboard_check(ord('D')) and !keyboard_check(ord('A')) { if place_free(x + global.PlayerMoveSpeed,y) { //Der Spieler hält also A und nicht D gedrückt. global.PlayerIdleLeft = 0 global.PlayerIdleRight = 0 global.PlayerMoveLeft = 0 global.PlayerMoveRight = 1 //Der Player rennt nach Rechts. x += global.PlayerMoveSpeed //Bewegt den Spieler in die angegebene Richtung. } else { global.PlayerIdleLeft = 0 global.PlayerIdleRight = 1 //Rechts ist eine Wand? Bleib stehen und guck nach Rechts. global.PlayerMoveLeft = 0 global.PlayerMoveRight = 0 } } if keyboard_check(ord('D')) and keyboard_check(ord('A')) { global.PlayerIdleLeft = 0 global.PlayerIdleRight = 1 //Rechts ist eine Wand? Bleib stehen und guck nach Rechts. global.PlayerMoveLeft = 0 global.PlayerMoveRight = 0 }
      7. if keyboard_check_pressed(ord('W')){ if !place_free(x,y + 1) { vspeed = -global.PlayerJumpFirst } else if keyboard_check_pressed(ord('W')) && global.PlayerJumpTwice = 1 { global.PlayerJumpTwice = 0 vspeed = -global.PlayerJumpSecond }}
      8. if keyboard_check_released(ord('W')){ if vspeed < 0 { vspeed /= 2 }}


      evtl. könnte die Block-Kollision auch irgentwie die Ursache sein?

      GML-Quellcode

      1. if vspeed > 0 && !place_free(x,y + vspeed) { global.PlayerJumpTwice = 1 move_contact_solid(direction,vspeed) }
      2. vspeed = 0


      Im .rar Archiv im Anhang gibt es zudem noch die Codes ohne .exe, für die die einen schnelleren Download haben wollen und ungefähr wissen was ich meine.

      LG Marvin159
      Dateien

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

    • Für Kollisionen solltest du x- und y-Koordinaten runden. Je nach "Engine" hilft das ungemein.
      Weiterhin ist es nicht verwunderlich, dass die Figur sich drüberbewegt: Bewegt die Figur sich mehr als einen Pixel weit pro Step, wird sie evtl. vorher UND nachher festen Boden unter den Füßen haben.
    • MewX schrieb:

      Für Kollisionen solltest du x- und y-Koordinaten runden. Je nach "Engine" hilft das ungemein.
      Weiterhin ist es nicht verwunderlich, dass die Figur sich drüberbewegt: Bewegt die Figur sich mehr als einen Pixel weit pro Step, wird sie evtl. vorher UND nachher festen Boden unter den Füßen haben.
      ah alles klar, soweit hab ich verstanden was du meinst.. allerdings weiß ich nicht wie es möglich ist die koordinaten zu runden :/ soll ich da mit einen Grid arbeiten :o ?

      LG Marvin159

      EDIT:// ein Grid war dann wohl nicht die Lösung..
    • Das geht z.B., indem du in den betreffendne Teilen deines Codes so vorgehst:

      GML-Quellcode

      1. var xx, yy;
      2. xx = round(x); // vielleicht auch floor()
      3. yy = round(y);
      4. /*
      5. Rufe alle Kollisionsfunktionen mit xx und yy anstelle von x und y auf.
      6. Vielleicht ist es auch schlauer, die ursprünglichen Positionen zu speichern, x und y zu runden und dann auf ihnen direkt zu arbeiten.
      7. */
      8. // Je nach Ergebnis überträgst du dann xx,yy unmittelbar auf x und y zurück (bei einer Positioskorrektur)
      9. x = xx;
      10. y = yy;
      11. // oder arbeitest mit z.B. den errechneten Verschiebungen
      12. x += xoff;
      13. y += yoff;
      Alles anzeigen

      Merke aber: Die Möglichkeiten sind unbegrenzt und es gibt kein Patentrezept. Nimm diesen Lösungsvorschlag also unter keinen Umständen als allgemeingültig hin - vielleicht ist er für dein Spiel völlig unbrauchbar.

      Aber das ist nunmal die große Schwachstelle des GM: Dadurch, dass die Bewegungs-/Kollisionsengine vorgegeben ist, macht man sich selbst viel zu wenig Gedanken über solche Dinge und stellt am Ende eher hässliche Dinge mit der bestehenden Engine an.


      Ich weiß auch nicht, ob das immer noch so ist, aber wenn man das Spiel skaliert und z.B. den Spieler auf x=12,5 zeichnet, dann wird er auch wirklich zwischen 12 und 13 gezeichnet. Für viele Spiele ist das natürlich unerwünscht. Für z.B. Odem Forest lasse ich daher im Draw-Event einmal runden, wenn ich mich recht entsinne.
    • MewX schrieb:

      Das geht z.B., indem du in den betreffendne Teilen deines Codes so vorgehst:

      GML-Quellcode

      1. var xx, yy;
      2. xx = round(x); // vielleicht auch floor()
      3. yy = round(y);
      4. /*
      5. Rufe alle Kollisionsfunktionen mit xx und yy anstelle von x und y auf.
      6. Vielleicht ist es auch schlauer, die ursprünglichen Positionen zu speichern, x und y zu runden und dann auf ihnen direkt zu arbeiten.
      7. */
      8. // Je nach Ergebnis überträgst du dann xx,yy unmittelbar auf x und y zurück (bei einer Positioskorrektur)
      9. x = xx;
      10. y = yy;
      11. // oder arbeitest mit z.B. den errechneten Verschiebungen
      12. x += xoff;
      13. y += yoff;
      Alles anzeigen

      Merke aber: Die Möglichkeiten sind unbegrenzt und es gibt kein Patentrezept. Nimm diesen Lösungsvorschlag also unter keinen Umständen als allgemeingültig hin - vielleicht ist er für dein Spiel völlig unbrauchbar.

      Aber das ist nunmal die große Schwachstelle des GM: Dadurch, dass die Bewegungs-/Kollisionsengine vorgegeben ist, macht man sich selbst viel zu wenig Gedanken über solche Dinge und stellt am Ende eher hässliche Dinge mit der bestehenden Engine an.


      Ich weiß auch nicht, ob das immer noch so ist, aber wenn man das Spiel skaliert und z.B. den Spieler auf x=12,5 zeichnet, dann wird er auch wirklich zwischen 12 und 13 gezeichnet. Für viele Spiele ist das natürlich unerwünscht. Für z.B. Odem Forest lasse ich daher im Draw-Event einmal runden, wenn ich mich recht entsinne.
      Also ich habe in den Place-free abfragen nun mit xx und yy gearbeitet. Am Kopf des Codes befindet sich folgender Teil :


      var xx, yy;
      xx = round(x);
      yy = round(y);

      Sprich die Variablen werden erstellt und ihnen wird der gerundete Wert zugewiesen. Soweit so gut. In Den Abfragen wird wie gesagt nun mit diesen Variablen auch gearbeitet :


      if keyboard_check(ord('A')) and !keyboard_check(ord('D'))
      {
      if place_free(xx - global.PlayerMoveSpeed,yy)
      {
      blubb..
      }
      }

      Allerdings habe ich beim Bewegen mit hspeed und nicht mit "jump to position" gearbeitet wie ichs sonst immer mache und daher weiß ich jetzt auch nicht direkt wie ich es umsetzen soll.

      Denn wenn ich nach dein Beispiel arbeite kommt der Charakter zwar immer über diese Abgründe (es sei denn man bleibt darüber stehen) rüber und den Player wird die Fall-Sprite zugewiesen. Diese wird ihn aber eigentlich nur zugewiesen, wenn der vspeed < 0 ist, daher verstehe ich nicht sorecht wie es dazu kommen kann :huh:

      MfG Marvin159
    • Vll. auch so zu lösen.

      Das war schon die Antwort:

      Bewegt die Figur sich mehr als einen Pixel weit pro Step, wird sie evtl. vorher UND nachher festen Boden unter den Füßen haben.


      Ich würds so lösen:

      Wenn dein Spieler ne Collision mit dem Abgrund hat dann verkleinere die Collisionsabfrage mit "collision_circle" und ner Variablen darin für die Spielfigur. Dann stürzt er sogar schon wenn er über 50% im Abgrund steht. Dann passt er auf jeden Fall bei Bewegung in das Loch.

      Gruss
      Aktuelles Game: "Pikslar" mit Online Hiscore





      PS: Will mit dieser Frage mal fragen, ob jemand zur zeit, Zeit hat. Suche Grafiker/Pixler










    • Wildor schrieb:

      Vll. auch so zu lösen.

      Das war schon die Antwort:

      Bewegt die Figur sich mehr als einen Pixel weit pro Step, wird sie evtl. vorher UND nachher festen Boden unter den Füßen haben.


      Ich würds so lösen:

      Wenn dein Spieler ne Collision mit dem Abgrund hat dann verkleinere die Collisionsabfrage mit "collision_circle" und ner Variablen darin für die Spielfigur. Dann stürzt er sogar schon wenn er über 50% im Abgrund steht. Dann passt er auf jeden Fall bei Bewegung in das Loch.

      Gruss
      Also im GM sieht der ganze .exe Beispiel room nun so aus... Der Player hat nur eine Kollision mit den weißen Blöcken.
      Die Hitbox vom Player ist 8x16 groß.
      Sprich der Abgrund ist doppelt so breit wie der Player.

      Und nun meine Frage :
      Ist es auch irgentwie möglich das der Player nicht abstürzt wenn er grade eben noch auf dem Block steht ?

      Aufjedenfall schonmal vielen Dank an MewX und Wildor für die schnellen Antworten :)
    • Das wird wieder ein Post mit vielen Gedankengängen.
      Wenn du bei manchen noch zwei Schritte weiterdenkst, sieht die Lösung am Ende vielleicht doch anders aus, als der erste Satz suggiert. Sprich: Ich habe nicht alle davon zu Ende gedacht.


      In Trippelschritten arbeiten
      Wenn die Figur horizontal bewegt wird bzw. wurde (kommt darauf an, was für deine Engine besser passt), durchläufst du eine for-Schleife und für jeden voll bewegten Pixel machst du eine Kollisionsabfrage nach unten hin.
      Bei einer hohen Laufgeschwindigkeit bedeutet das natürlich deutlich mehr Rechenaufwand.


      Die überlaufene Zelle lokalisieren
      Wenn sich alle Wände in einem Grid befinden, musst du nur abfragen, ob der Spieler sich zu einem Zeitpunkt über eine Zelle hinwegbewegt und diese dann zusätzlich abfragen (wenn der Spieler so groß wie eine Zelle ist, kann das ja schon bei einer Geschwindigkeit von 2 passieren).
      Die überlaufene Zelle könntest du z.B. berechnen, indem du (bei einer Bewegung nach rechts) den linken Rand der Spielfigurmaske vor der Bewegung ganzzahlig durch die Gridbreite teilst und dann mit der Zahl vergleichst, die du für den rechten Rand der Spielfigur nach der Bewegung ebenso berechnest. Ist die Differenz größer als 1, hast du die dazwischen liegende Zelle übersprungen.
      Das könntest du dann auch recht cool in eine For-Schleife packen, die gar nicht erst ausgeführt wird, wenn die Differenz kleiner als 2 ist:

      GML-Quellcode

      1. var i;
      2. // Für Bewegungen nach rechts
      3. for (i=(bbox_left div gridbreite) + 1; i<((bbox_right+xspeed) div gridbreite); i+=1) {
      4. if (place_free(i * gridbreite, y)) {
      5. /* weiteres Handling */
      6. break; // Mehr Abfragen braucht es schließlich nicht
      7. }
      8. }

      Für den linken und rechten Rand der Spielfigur eignen sich bbox_left und bbox_right.
      Es muss natürlich eine Fallunterscheidung für die beiden Bewegungsrichtungen gemacht werden (wenn du nicht gut mit Vorzeichen tricksen kannst, weiß ich grad nicht).


      Eine andere Idee mit Zellen
      Du kannst das aber auch vielleicht auf einem anderen Weg lösen (Grid vorausgesetzt).
      Du fragst nur die Zelle ab, in der sich die größere Teil der Spielfigur nach der Bewegung befinden würde. Für genau diese Zelle machst du dann die Abfrage. Das bedeutet natürlich, dass wenn sich mehr als die Hälfte der Spielfigur über einem Abgrund befindet, diese auch sofort da rein fällt. Da man aber meistens noch etwas Toleranz haben will, solltest du voraussetzen, dass sich mindestens n Pixel in der entsprechenden Zelle befinden müssen.
      Vergiss dann aber natürlich nicht, die x-Position des Spielers entsprechend dem Grid anzupassen, wenn er einen schmalen Schacht runterfallen soll. Sonst hängt er in der Wand.


      Fazit
      Ich hoffe, ich habe dir hiermit einen guten Zugang zu dem abstraktem Denken dahinter gegeben, sollte er dir bislang gefehlt haben.
      Lass dich nie verrückt machen. Irgendwann bist du unter Umständen so in Gedankengängen versunken oder mit den verfügbaren Möglichkeiten überfordert, dass du das Ziel aus den Augen verlierst.
      Vergiss nie: Deine Engine muss nur das können, was für das Spiel wichtig ist.
      Mach dir klar, wo die Grenzen deiner Anforderungen liegen. Bewegt die Spielfigur sich jemals schneller als das Grid breit ist? Dann musst du auch nie Angst haben, dass sich deine Spielfigur durch Wände bewegen könnte.


      Und achja: Wenn du alte Positionen speichern musst, lass lieber die Finger von xprevious, da dies nur zu einem ganz bestimmtem Zeitpunkt gesetzt wird (zwischen Step und Endstep, glaub ich).
      Auch wirst du vermutlich auf die eingebauten Speed-Variablen verzichten wollen, da es sonst zu Chaos kommt. Schau einfach, was passt.


      Ich werde das Thema auch mal in die Expertenrunde verschieben, da hier doch alles sehr abstrakt ist.