Du bist nicht angemeldet.

Werbeanzeige

1

30.12.2019, 19:11

Problem bei der Kollision zwischen zwei Objekten SDL2 C++

Hallo Leute :) ,
Ich bin dabei ein "Jump 'n Run" Spiel zu programmieren und habe da ein kleines Problem mit der Kollision und versuche schon seit längerem dieses Problem zu lösen.
Also die Kollisionserkennung an sich funktioniert schonmal nur konnte ich jedes mal trotzdem durch andere objekte durchlaufen also habe ich in einem Neuem Projekt mit der Kollision von zwei Rechtecken rumgespielt.
Nun bin jetzt dazu gekommen das ich zwar gestoppt werde wenn ich gegen das Objekt komme nur bleibe ich sozusagen dran kleben und komm nicht mehr weg.



Collision funktion:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void CGame::Collision()
{
    SDL_Rect A;
    SDL_Rect B;
    vector<CSolid>::iterator it = Solidlist.begin();
    B = Rect->GetRect();
    for (it; it != Solidlist.end(); it++)
    {
        A = it->GetRect();

        if (Checkcollision(A,B))
        {
                        //Sides Check
            if (B.x <= A.x + A.w || B.x + B.w >= A.x)
            {
                Rect->Set();
            }
                        //Top&Bottom Check
            if (B.y < A.y + A.h || B.y + B.h > A.y)
            {
                
            }
        }
    }

}


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool CGame::Checkcollision(SDL_Rect RectA, SDL_Rect RectB)
{
        int Atop = RectA.y;
        int ABottom = RectA.y + RectA.h;
        int ALeft = RectA.x;
        int ARight = RectA.x + RectA.w;
    
        int Btop = RectB.y;
        int BBottom = RectB.y + RectB.h;
        int BLeft = RectB.x;
        int BRight = RectB.x + RectB.w;
        
        return Atop <= BBottom &&
            ABottom >= Btop &&
            ALeft <= BRight &&
            ARight >= BLeft;
}



Set Funktion:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
void CRect::Set()
{
    m_newxPos = m_xPos;
       //Was ich schon versucht habe ist 
       //m_newxPos = m_xPos - 1.0f; Dann komm ich zwar vom Solidem Block weg. Wenn ich jedoch 
       bewegt sich der Spieler(Rect) hin und her (GIF im anhang was ich genau meine)

}


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
void CRect::Update()
{
    m_xPos = m_newxPos;
    m_yPos = m_newyPos;

    Move();

    m_newxPos = m_xPos + VelX * Timer->TimeElapsed();
    m_newyPos = m_yPos + VelY * Timer->TimeElapsed();
}


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void CRect::Move()
{
    
        if (g_pFramework->KeyDown(SDL_SCANCODE_UP))
        {
            VelY = -150.0f;
        }
        else if (g_pFramework->KeyDown(SDL_SCANCODE_DOWN))
        {
            VelY = 150.0f;
        }
         else if (g_pFramework->KeyDown(SDL_SCANCODE_LEFT))
        {
            VelX = -150.0f;
        }
        else if (g_pFramework->KeyDown(SDL_SCANCODE_RIGHT))
        {
            VelX = 150.0f;
        }
        else {
            VelX = 0.0f; VelY = 0.0f;
        }
        
}


Im Anhang gibts noch zwei GIF´s um das ganze etwas visueller darzustellen :)
Ich bedanke mich im voraus für eure Hilfe. :D
»phizle« hat folgende Bilder angehängt:
  • ezgif.com-video-to-gif.gif
  • ezgif.com-video-to-gif (1).gif

2

31.12.2019, 01:30

Also in c++ kenne ich mich leider überhaupt nicht aus. Aber anhand von den beispielen könnte ich vielleicht eine Fehlerquelle aus machen ^^
Im Prinzip ist das ja eine ziemlich simple art der Kollisionserkennung :)
Du musst für jedes Rechteck jeweils 8 ecken abfragen.

1. Obere Linke Ecke
2. Oberste Länge
3. Obere Rechte Ecke
4. Linke Länge
5. Rechte Länge
6. Untere Linke ecke
7. Untere Länge
8. Untere Rechte Ecke
»Timmyewr« hat folgendes Bild angehängt:
  • beispiel.png

3

31.12.2019, 17:52

Es wäre praktisch, den gesamten Code (am besten als geziptes Projekt) hier bereitzustellen, damit man sehen kann, in welcher Reihenfolge CGame::Collision(), CRect::Update() und das Zeichnen der Rechtecke ausgeführt wird.

Zuerst einmal noch eine kleine Anmerkung zu deinem Code:
Statt

C-/C++-Quelltext

1
2
3
4
5
6
vector<CSolid>::iterator it = Solidlist.begin();
for (it; it != Solidlist.end(); it++)
{
    A = it->GetRect();
    // Mach was mit A
}

kannst du auch einfach

C-/C++-Quelltext

1
2
3
4
for (const auto& solid : Solidlist)
{
    // Verwende solid.GetRect(); Oder schreibe A = solid.GetRect() und verwende dann A
}

schreiben. Das nennt sich range-for und existiert seit C++11 (range-based for loop).

Nun zu deinem eigentlichen Problem:
Das Problem liegt höchstwahrscheinlich in der Reihenfolge, in der die Dinge passieren. Mehr kann ich dazu aber nicht sagen, ohne nicht den gesamten Code gesehen zu haben.
Ich würde aber jetzt einfach mal sagen, dass die allgemeine Abfolge so aussehen sollte:
1. User Inputs verarbeiten (also die Geschwindigkeiten setzen)
2. Spieler bewegen
3. Kollisionsabfrage zwischen 'Player' und Umgebung:
-- Wenn Kollision: Position des Spielers so setzen, dass KEINE Kollision mehr stattfindet ohne User-Input (siehe Anhang 1 zur genaueren Ausführung)
-- Wenn keine Kollision: alles in Ruhe lassen
4. Objekte zeichnen

Dabei brauchst du außerdem nur eine x- und y-Pos und keine newPos mehr. Es wird einfach immer die aktuelle Position des Spielers manipuliert, und die Änderungen werden eh erst mit dem Zeichnen sichtbar.

Anhang 1:
Ich würde es so machen, dass es erst eine Kollision ist, wenn die Objekte ineinander sind, und im Fall einer Kollision die Position so setzen, dass sich die Objekte berühren, aber nicht überschneiden. Das heißt, bei der Kollisionsabrafe aus den <= ein < machen, und der Funktion Set() einfach eine neue x und eine neue y Position übergeben. Die neue Position muss je nachdem wo die Kollision stattfand extra berechnet werden. (Bedenke: wenn der Player mit einer Ecke kollidiert, müssen sowohl die Änderungen in x und die Änderungen in y Richtung gemacht werden.)

Das ist dann etwas komplexer, da man sozusagen richtig entscheiden muss, wann die Kollision nur von oben, wann von links, wann von rechts unten, usw., kam. Denn je nachdem, muss die neue Position gesetzt werden. Am einfachsten hier wäre es aber, sich die letzte Position des Spielers ohne Kollision zu merken.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
B = Rect->GetRect();
bool didCollide = false;
for (const auto& solid : Solidlist)
{
    A = solid.getRect();

    if (Checkcollision(A,B))
    {
        didCollide = true;
    }
}

if (didCollide)
{
    Rect->SetPositionToLastPositionWithoutCollision();
}
else
{
    Rect->UpdateLastPositionWithoutCollision();
}


Wenn du aber simple Physik (Abprallen; bewegende Objekte, die einen verschieben; ...) implementieren willst, wird das sehr schnell recht aufwändig mit den ganzen if-Abfragen. Da bietet es sich dann an, den Vektor zwischen den beiden Objektmittelpunkten zu berechnen, dann den Schnittpunkt (= 'Aufprallpunkt') von diesem Vektor mit dem anderen Objekt zu berechnen, und mit diesem Vektor und Schnittpunkt die Objekte so bewegen, wie man es braucht (zum Beispiel einen Vektor bilden, der vom Schnittpunkt mit dem Eintrittswinkel weg geht => simples Abprallen).

Ich hoffe ich konnte helfen.

LG Patrick

Einen guten Rutsch und ein schönes neues Jahr!
Albert Einstein sagte: "2 Stunden mit einem netten Mädchen fühlen sich an wie 20 Minuten, 20 Minuten auf einem heißen Ofen fühlen sich an wie 2 Stunden. - Das ist Relativität"

4

31.12.2019, 20:37

@Patick Z. Erstmal vielen dank für deine Antwort :D
Ich werde mir das ganze morgen mal anschauen. Falls ich es dann trotzdem nich hinbekomme werde ich das ganze Projekt mal hochladen.

Danke, wünsche dir auch ein guten Rutsch und ein schönes neues Jahr :)


@Timmyewr Danke auch dir für deine Antwort.

Werbeanzeige