2D-Platformer-Kollisionsbehandlung

8

Ich versuche, ein 2D-Plattformspiel (Mario-Typ) zu erstellen, und ich habe einige Probleme mit dem richtigen Umgang mit Kollisionen. Ich schreibe dieses Spiel in C ++ und verwende SDL für die Eingabe, das Laden von Bildern, das Laden von Schriftarten usw. Ich verwende OpenGL auch über die FreeGLUT-Bibliothek in Verbindung mit SDL, um Grafiken anzuzeigen.

Meine Methode zur Kollisionserkennung ist AABB (Axis-Aligned Bounding Box), was wirklich alles ist, mit dem ich beginnen muss. Was ich brauche, ist eine einfache Möglichkeit, sowohl zu erkennen, auf welcher Seite die Kollision aufgetreten ist, als auch die Kollisionen richtig zu behandeln. Wenn der Spieler mit der Oberseite der Plattform kollidiert, positionieren Sie ihn im Grunde genommen nach oben. Wenn die Seiten kollidieren, positionieren Sie den Spieler wieder auf der Seite des Objekts. Wenn es zu einer Kollision mit dem Boden kommt, positionieren Sie den Spieler unter der Plattform neu.

Ich habe viele verschiedene Methoden ausprobiert , z. B. den Versuch, die Eindringtiefe zu ermitteln und den Player anhand der Eindringtiefe rückwärts zu positionieren. Leider scheint nichts, was ich versucht habe, richtig zu funktionieren. Die Bewegung des Spielers ist sehr unangenehm und positioniert den Spieler neu, wenn ich das nicht möchte. Ein Grund dafür ist wahrscheinlich, dass ich das Gefühl habe, dass dies etwas so Einfaches ist, aber ich überlege es mir.

Wenn jemand glaubt, helfen zu können, schauen Sie sich bitte den folgenden Code an und helfen Sie mir, dies zu verbessern, wenn Sie können. Ich würde gerne darauf verzichten, eine Bibliothek zu verwenden, um damit umzugehen (wie ich selbst lernen möchte) oder so etwas wie das SAT (Separating Axis Theorem), wenn dies überhaupt möglich ist. Vielen Dank im Voraus für Ihre Hilfe!

void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
    {
        de2dObj ballPrev;
        ballPrev.coords[0] = ball.coords[0];
        ballPrev.coords[1] = ball.coords[1];
        ballPrev.coords[2] = ball.coords[2];
        ballPrev.coords[3] = ball.coords[3];
        ballPrev.coords[0] -= ball.xspeed;
        ballPrev.coords[1] -= ball.yspeed;
        ballPrev.coords[2] -= ball.xspeed;
        ballPrev.coords[3] -= ball.yspeed;

        int up = 0;
        int left = 0;
        int right = 0;
        int down = 0;

        if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
        {
            left = 1;
        }

       if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
        {
            right = 1;
        }
        if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
        {
            up = 1;
        }
        if(block[i].coords[1] < ballPrev.coords[1] && ball
        {
            down = 1;
        }

        cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;

        if (left == 1)
        {
            ball.coords[0] = block[i].coords[0] - 18.0f;
            ball.coords[2] = block[i].coords[0] - 2.0f;
        }
        else if (right == 1)
        {
            ball.coords[0] = block[i].coords[2] + 2.0f;
            ball.coords[2] = block[i].coords[2] + 18.0f;
        }
        else if (down == 1)
        {
            ball.coords[1] = block[i].coords[3] + 4.0f;
            ball.coords[3] = block[i].coords[3] + 20.0f;
        }
        else if (up == 1)
        {
            ball.yspeed = 0.0f;
            ball.gravity = 0.0f;
            ball.coords[1] = block[i].coords[1] - 17.0f;
            ball.coords[3] = block[i].coords[1] - 1.0f;
        }
    }
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
    {
        ball.gravity = -0.5f;
    }
}
}

Um zu erklären, was ein Teil dieses Codes bedeutet:

Die Blockvariable ist im Grunde eine Ganzzahl, die die Anzahl der Blöcke oder Plattformen speichert. Ich überprüfe alle Blöcke mit einer for-Schleife, und die Nummer, auf der sich die Schleife gerade befindet, wird durch die Ganzzahl i dargestellt. Das Koordinatensystem mag etwas seltsam erscheinen, daher lohnt es sich, dies zu erklären. Koordinaten [0] repräsentieren die x-Position (links) des Objekts (wo es auf der x-Achse beginnt). Koordinaten [1] repräsentieren die y-Position (oben) des Objekts (wo es auf der y-Achse beginnt). Koordinaten [2] repräsentiert die Breite des Objekts plus Koordinaten [0] (rechts). Koordinaten [3] repräsentiert die Höhe des Objekts plus Koordinaten [1] (unten). de2dCheckCollision führt eine AABB-Kollisionserkennung durch. Hoch ist negativ y und runter ist positiv y, wie es in den meisten Spielen der Fall ist.

Hoffentlich habe ich genug Informationen bereitgestellt, damit mir jemand erfolgreich helfen kann. Wenn ich etwas ausgelassen habe, das von entscheidender Bedeutung sein könnte, lassen Sie es mich wissen und ich werde die erforderlichen Informationen bereitstellen. Für jeden, der helfen kann, wäre die Bereitstellung von Code sehr hilfreich und wird sehr geschätzt.

Nochmals vielen Dank für Ihre Hilfe!

Bearbeiten : Ich habe meinen Code mit einem neuen Algorithmus aktualisiert, der überprüft, wo sich der Ball vor der Kollision befand. Eckkästen funktionieren jetzt korrekt auf dieser einzelnen Plattform, aber wenn ich eine Wand aus Objekten habe, kann ich wirklich dagegen gleiten, aber wenn ich mich beim Gleiten zur Wand bewege, gehe ich durch sie hindurch und stehe im Wesentlichen jetzt auf einer Block innerhalb der Wand. Es gibt auch einen Jitter-Effekt, der auftritt, wenn ich am Boden bin und der Ball ständig auf und ab geht.

Verteidigerzone
quelle
Hast du deinen Jitter gelöst? Wenn nicht, ist es wahrscheinlich ein Problem, bei dem Sie Ihren Charakter zu weit bewegen, anstatt TheirPos-ourSize +- 1die 1 zu entfernen, wenn Sie dies tun. Wenn Sie sich nicht sicher sind, wie Sie diesen Algorithmus herausfinden können, wäre ich mehr als bereit zu helfen. Mein Algorithmus ist im Moment perfekt =] (auch in welcher Reihenfolge checken Sie die Objekte ein?)
ultifinitus

Antworten:

6

Wenn ich Ihren Code richtig verstehe, kompensieren Sie die Kollision, indem Sie den Ball um einen festen Betrag versetzen, ohne seine Geschwindigkeit zu beeinflussen. Der Grund, warum Ihr Ball nervös wirkt, ist, dass seine Position zwar aufgrund der Kollision korrigiert wird, seine Geschwindigkeit jedoch immer noch auf das kollidierende Objekt gerichtet ist. Aufgrund dieser Geschwindigkeit bewegt sich der Ball immer noch in Richtung des kollidierenden Blocks und kollidiert nach einigen Frames, die ihn wieder versetzen.

Die vielleicht einfachste Lösung besteht darin, sicherzustellen, dass die Geschwindigkeiten bei einer Kollision zurückgesetzt werden, und sicherzustellen, dass sich der Ball nach dem Versetzen nicht in Richtung des Blocks bewegt.

Geist
quelle
5

Sie zeigen keinen Code an, der die Bewegung / Geschwindigkeit des Balls handhabt. Wenn Sie den Ball nicht stoppen / abprallen lassen, bewegt sich Ihr Ball im nächsten Frame zurück in den Block und wird "zurückbewegt", sodass er wahrscheinlich an der Seite des Blocks "stecken bleibt".

Wenn dies nicht der Fall ist, wenn Sie die Probleme genauer beschreiben und / oder mehr Code veröffentlichen können, kann möglicherweise jemand besser helfen :-)

Danny Tuppeny
quelle
Wenn es hilft, habe ich den Code aktualisiert und einen neuen Algorithmus hinzugefügt, der jetzt überprüft, wo sich der Ball befand, bevor die Kollision aufgetreten ist. Das einzige verbleibende Problem ist, dass es einen kleinen Jittereffekt gibt, der auftritt, wenn ich am Boden bin, wo der Ball ständig auf und ab geht, als würde er durch die Schwerkraft gezogen, und dann fällt der Ball wieder in das Objekt zurück.
Verteidigerzone
@ Verteidiger-Zone Sie möchten den Ball nicht zurück in die vorherige Position bringen. Berechnen Sie lieber die Überlappung des Balls und des Hindernisses, wenn eine Kollision auftritt, und bewegen Sie den Ball um den berechneten Betrag (Überlappung) vom Hindernis weg.
Bummzack
4

Nun, Sie können immer das tun, was ich getan habe, auch wenn es von meiner Seite schlecht implementiert wurde. Ich habe eine einfache Methode in meiner "Being" -Klasse namens update_prev_pos (), die genau das tut. Sie speichert Ihre aktuelle Position in einer Struktur, die Sie sich ansehen möchten später. In meiner Kollisionsauflösungsfunktion betrachte ich die Position der betreffenden Seite. Wenn sich die Seite zuvor außerhalb des Objekts befand, muss diese Seite korrigiert werden.

Ich verwende eine Zwei-Pass-Methode, um Kollisionen zu überprüfen und aufzulösen. Ich würde empfehlen, alle Kollisionen der x-Achse und dann alle Kollisionen der y-Achse zu überprüfen. Auf diese Weise bleiben Sie nicht an Objekten hängen, die Sie nicht sollten. glücklich kollidieren =)

Ultifinitus
quelle
1

Vielen Dank für all die Hilfe, Leute, und entschuldigen Sie die späte Antwort.

Ich habe all diese Ideen berücksichtigt und sie helfen mir, das Problem zu beheben. Wenn jemand neugierig ist, wie ich das ursprüngliche und vollständige Problem gelöst habe, finden Sie hier auf Stapelüberlauf die vollständige Antwort (einschließlich Erläuterungen zum besseren Umgang mit Kollisionen). Die Person, die meine Frage beantwortete, war sehr großzügig und lieferte ein Diagramm, das denjenigen helfen sollte, die auch dieses Problem haben.

Vielen Dank an die folgenden Personen, die mir bei diesem Problem geholfen haben:

  • DanTup - Danny Tuppeny
  • bummzack
  • Ghostonline
  • Ultifinitus

Ich schätze es sehr!

Verteidigerzone
quelle
5
Ich denke, es ist besser, wenn Sie den Menschen, die Ihnen geholfen haben, Anerkennung zollen, als Ihre eigene Antwort zu erstellen, als sie als Antwort zu markieren. Geben Sie ihnen zumindest eine Gegenstimme. Nur meine zwei Cent.
DMan
Das ist eine gute Idee; Ich habe nicht darüber nachgedacht. Ich entschuldige mich dafür, aber ich habe noch nicht einmal genug Ruf, um mich zu verbessern. Wenn ich es aber bekomme, werde ich es tun.
Verteidiger-Zone
@ Defender-Zone - Es ist nichts gegen dich. Ich habe mir die Freiheit genommen und alle Antworten für Sie positiv bewertet.
DMan
Vielen Dank dafür. Anscheinend habe ich gerade jetzt genug Ruf erhalten, um dank Ihrer Gegenstimme zu stimmen, also habe ich es auch getan. Ich habe auch diejenigen gewürdigt, die bei meiner Antwort geholfen haben.
Verteidiger-Zone
@ Defender-Zone - Haha, das ist ziemlich süß. Ich erinnerte mich nicht, dass das der Fall sein würde!
DMan