Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

01.10.2014, 18:27

Kollisionen an Ecken - [SDL]

Hallo Liebe Spieleprogrammierer,

ich habe folgendes Problem. Ich bin gerade dabei die Kollisionen eines Spieles zu programmieren.
Dies gelingt mir auch eigentlich recht gut. Das Problem ist nur wenn ich mit meinem Player an eine Ecke
eines Objektes stoße, wird mein Player verschoben.... ehrlich gesagt weiß ich nicht wie ich das Problem besser erklären soll deshalb habe
ich ein ein paar Screens gemacht.

Foto 1 & 2 zeigt eigentlich nur dass die Kollision an einer gerade Fläche keine probleme macht.

Foto 3 hingegen ist das Problem. Bewege ich nun meinen wunderschönen Player nach unten werde ich im nächsten moment auf die Position, welche Foto 4 zeigt, bewegt.

So und um alles abzurunden natürlich noch mein Code dazu:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void CPlayer::CheckCollision()
{
    for (vector<CSprite*>::iterator i = Walls.begin(); i != Walls.end(); i++)
    {
        PTop = pPlayer->GetRect().y;
        PBottom = pPlayer->GetRect().y + pPlayer->GetRect().h;
        PLeft = pPlayer->GetRect().x;
        PRight = pPlayer->GetRect().x + pPlayer->GetRect().w;

        OTop = (*i)->GetRect().y;
        OBottom = (*i)->GetRect().y + (*i)->GetRect().h;
        OLeft = (*i)->GetRect().x;
        ORight = (*i)->GetRect().x + (*i)->GetRect().w;

        if (SDL_HasIntersection(&pPlayer->GetRect(), &(*i)->GetRect()) == true)
        {

            if (PTop <= OBottom && PBottom >= OBottom )
            {
                Differenz = OBottom - PTop;
                MoveY = MoveY + Differenz;
            }
            if (PBottom >= OTop && PTop <= OTop )
            {
                Differenz = PBottom - OTop;
                MoveY = MoveY - Differenz;

            }
            if (PLeft <= ORight && PRight >= ORight)
            {
                Differenz = ORight - PLeft;
                MoveX = MoveX + Differenz;
            }

            if (PRight >= OLeft && PLeft <= OLeft)
            {
                Differenz = PRight - OLeft;
                MoveX = MoveX - Differenz;
            }

            cout << Differenz << endl;
        }
    }
}


Ich glaube der Fehler ist das Sobald ich mit meinem Player ein Objekt, sagen wir mal Oben Links wie auf den Fotos, berühre die If abfrage von PBottom und auch von PRight ausgelöst wird....
doch wie kann ich diese abfragen trennen damit es nicht mehr zu diesem Problem kommt?!


Liebe grüße Urprimat :ninja:
»Urprimat« hat folgende Dateien angehängt:
  • 1.bmp (481,08 kB - 63 mal heruntergeladen - zuletzt: Gestern, 12:44)
  • 2.bmp (240,12 kB - 59 mal heruntergeladen - zuletzt: Gestern, 15:14)
  • 3.bmp (481,08 kB - 59 mal heruntergeladen - zuletzt: 02.03.2024, 00:26)
  • 4.bmp (481,08 kB - 67 mal heruntergeladen - zuletzt: 22.06.2024, 19:59)
Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.

Linus Torvalds

Lares

1x Contest-Sieger

  • Private Nachricht senden

2

01.10.2014, 19:50

Dein Problem ist, dass du keine Fallunterscheidung machst, abhängig davon, von welcher Seite der Spieler mit dem Objekt kollidiert. Wenn der Spieler auf dem Objekt steht, sollte er nicht mit der Seite des Objekts kollidieren können. Es gibt verschiedene Möglichkeiten diese Fallunterscheidung zu implementieren. Man könnte die absoluten Positionen der Objekte vergleichen (momentan vergleichst du ja nur die Seiten) oder über einen Strahl zwischen beiden Positionspunkten. Man könnte auch versuchen mittels eines Richtungsvektors für den Player zu bestimmen, aus welcher Richtung er zum Zeitpunkt der Kollision kam. Diese Methode kann aber schnell zu Fehlern führen, da dieser Vektor von verschiedenen Dingen beeinflusst werden muss (Gravitation zum Beispiel).

3

02.10.2014, 08:19

Hey guten Morgen und erstmal Danke für die Antwort.
Nur leider versteh ich nicht ganz wie ich an die Sache rangehen soll....
was bringt mir das vergleichen der absoluten Postion der Objekte?
ich dachte nämlich ich unterscheide schon durch meine if abfragen aus welcher Seite der Spieler kommt.
Insofern unterscheidet er doch ob der Spieler von Rechts oder Links kommt da er sogar wieder zurück gestoßen wird.
Oder verstehe ich das Falsch?!

Mfg Urprimat :ninja:
Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.

Linus Torvalds

Lares

1x Contest-Sieger

  • Private Nachricht senden

4

02.10.2014, 14:38

Ich seh gerade ich hab mich stark mssverständlich ausgedrückt. Ich versuchs nochmal:

Du prüfst momentan ob die Objekte ineinander liegen, nicht direkt die Seite als solches, lediglich einzelne Koordinaten davon.
Eine Linie/Kante/Seite besteht aus zwei Punkten und ein Punkt aus einem X-Wert und einen Y-Wert.
Vergleichst du in einer einzelnen Abfrage den X-Wert und Y-Wert zweier Punkte mit zwei anderen Punkten? Soweit wie ich das sehe nicht.

Nehmen wir mal folgende Werte an:
Player {x=0, y=0, Weite=32,Höhe=32}
Objekt {x=16, y=0, Weite=32, Höhe=32}

Der Player wäre somit weiter links als das Objekt. Da beide den gleichen y Wert haben, sollte dein Code die Spielfigur nur nach links korrigieren.

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
27
28
29
30
PTop = 0;
PBottom = 0 + 32;
PLeft = 0;
PRight = 0 + 32;

OTop = 0;
OBottom = 0 + 32;
OLeft = 16;
ORight = 16 + 32;

if (PTop <= OBottom && PBottom >= OBottom ) // 0 <= 32 && 32 >= 32
{
 //wird ausgeführt
  Differenz = 32 - 0;
  MoveY = MoveY + 32; //wobei ich mich hier gerade eh frage woher moveX und moveY kommen bzw. was die für Werte haben
}
if (PBottom >= OTop && PTop <= OTop )  //32 >= 0 && 0 <= 0 ==> wird ausgeführt
{
 //wird ausgeführt
}

if (PLeft <= ORight && PRight >= ORight) // 0 <= 48 && 32 >= 48
{
 //wird nicht ausgeführt
}

if (PRight >= OLeft && PLeft <= OLeft) //32 >= 16 && 0 <= 16
{
 //wird ausgeführt
}


Wie du siehst werden hier beide Abfragen für die Y-Korrektur ausgeführt, obwohl dies eigentlich nicht nötig sein sollte, da der Spieler von links an das Objekt stößt. Das liegt eben daran, dass du nicht die Seiten als solches, sondern eben nur einzelne Koordinaten dessen vergleichst.

Du könntest natürlich nun lediglich deine Abfragen so umformen, dass sie tatsächlich die Seiten von Spielfigur und Objekt überprüfen, aber auch das ist nicht der beste Weg.
Es gibt das so genannte Bullet-Through-Paper-Problem.
Ein Objekt, dass sich schneller als 1 Pixel pro Frame bewegt, kann unter Umständen bereits in einem anderen Objekt liegen, bevor es zu Kollisionsprüfung kommt.
Dadurch könntest du dich nicht mehr auf eine bloße Seitenüberprüfung verlassen, da die Spielfigur ja bereits zu weit im Objekt feststecken könnte und die Seiten als solches vllt. nicht mehr miteinander kollidieren.

Dementsprechend musst du miteinbeziehen, aus welcher Richtung der Spieler kommt. Das kannst du unter anderem machen indem sowohl die Position aus diesem Frame und den vorherigen speicherst.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Lares« (02.10.2014, 14:55)


5

04.10.2014, 16:07

Hey =)

Jetzt verstehe ich was du meinst perfekt das hat mir sehr weitergeholfen :search:

ich werd den Player jt sozusagen Tracken lassen und mein bestes verschen ich Poste dir dann irgendwann meine Ergebnisse =)
Vielen Dank =)

MFG Urprimat :ninja:
Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.

Linus Torvalds

6

07.10.2014, 20:08

Vielleich eine Lösung?

Hey wie versprochen meine Ergebnisse.... leider gibt es immernoch ein paar Probleme.

Als erstes Lasse ich im GameLoop den Spieler Tracken umd später ein Vergleich aus dem vorherigen Frame zu haben.

C-/C++-Quelltext

1
2
3
4
5
6
void CPlayer::TrackPlayer()
{
    OldRect.x = pPlayer->GetRenderRect().x;
    OldRect.y = pPlayer->GetRenderRect().y;

}


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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
void CPlayer::CheckCollision()
{

    for (vector<CSprite*>::iterator i = Walls.begin(); i != Walls.end(); i++)
    {
        PTop = pPlayer->GetRenderRect().y;
        PBottom = pPlayer->GetRenderRect().y + pPlayer->GetRenderRect().h;
        PLeft = pPlayer->GetRenderRect().x;
        PRight = pPlayer->GetRenderRect().x + pPlayer->GetRenderRect().w;

        OTop = (*i)->GetRenderRect().y;
        OBottom = (*i)->GetRenderRect().y + (*i)->GetRenderRect().h;
        OLeft = (*i)->GetRenderRect().x;
        ORight = (*i)->GetRenderRect().x + (*i)->GetRenderRect().w;

        if (isColliding() == true) //Funktion weiter unten
        {
            if (OldRect.x == pPlayer->GetRenderRect().x) //Falls es kein unterschied zwischen der alten & neuen X-Position gibt, muss der Spieler von Oben oder Unten kommen.
            {

                if (PTop <= OBottom && PBottom >= OBottom)
                {

                    Differenz = OBottom - PTop;
                    MoveY = MoveY + Differenz;
                }

                if (PBottom >= OTop && PTop <= OTop) 
                {

                    Differenz = PBottom - OTop;
                    MoveY = MoveY - Differenz;
                    cout << Differenz << endl;
                }
            }


            else if (OldRect.y == pPlayer->GetRenderRect().y)//Falls es kein unterschied zwischen der alten & neuen Y-Position gibt, muss der Spieler von Links oder rechts kommen.
            {

                if (PLeft <= ORight && PRight >= ORight)
                {

                    Differenz = ORight - PLeft;
                    MoveX = MoveX + Differenz;
                }

                if (PRight >= OLeft && PLeft <= OLeft)
                {

                    Differenz = PRight - OLeft;
                    MoveX = MoveX - Differenz;
                    cout << Differenz << endl;
                }
            }

        }

    }

}

bool CPlayer::isColliding()//Prüft ob im Umkehrschluss ob sich die Achsen überlappen.
{
    if (PBottom <= OTop)
    {
    return false;
    }
    else if (PTop >= OBottom)
    {
    return false;
    }

    else if (PRight <= OLeft)
    {
    return false;
    }

    else if (PLeft >= ORight)
    {
    return false;
    }

    return true;

}


So im groben klappt es besser als am Anfang nur habe ich nun das Problem wenn ich IN eine Ecke laufe (also das Negativ der alten Situation), der Player in die Textur geschoben wird... :dash:
Immoment arbeite ich mit der SDL_Rect struct die nur Int´s enthält.

Meinen Charakter bewege ich mit Floats:

C-/C++-Quelltext

1
2
3
4
    if (MoveUp)
        {
            MoveY -= 500.0f * Timer->GetElapsed();//gibt einen Zeitwert des letzen Frames in Sek wieder Z.b. 0,0015
        }


kann es sein, dass es dadurch zu Rundungsfehler kommt und somit OldRect und mit der Aktuellen Rect auf beiden Achsen identich ist und somit beide Abfragen stimmen?!
Ich habe ein wening rumgetüfftelt und versucht die SDL_Rect durch eigene float struct zu ersetzen... jedoch ohne erfolg :hmm:

ich bin echt nicht der beste im erklären :crazy: ich hoffe du verstehst mich und hast einen kleinen Rat 8)

Mfg Urprimat :ninja:
Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.

Linus Torvalds

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Urprimat« (07.10.2014, 20:15)


Lares

1x Contest-Sieger

  • Private Nachricht senden

7

08.10.2014, 09:54

Ist jetzt nur eine erste Analyse, deswegen weiß ich nicht, ob es den eigentlichen Fehler behebt:

In deiner Methode isColliding() versuchst du ja herauszufinden, ob eine Kollision auftritt. Die Abfragen (PTop >= OBottom) und (PLeft >= ORight) deuten aber auf eine Kollision hin (müssen nicht sein, können aber). Du solltest generell nicht >= oder <= verwenden, sondern das = weglassen, wenn du prüfen willst ob keine(!) Kollision auftritt. Andernfalls würdest du ja genau den Fall als Kollision ausschließen, bei dem eine exakte Überschneidung auftritt. Du solltest schon SDL_HasIntersection(&pPlayer->GetRect(), &(*i)->GetRect() aus deiner vorherigen Lösung zur Kollisionsprüfung verwenden können, das müsste genau diese Feinheiten beachten.
Ohne jetzt ne Garantie für den Erfolg geben zu können, solltest du mal versuchen isColliding durch SDL_HasIntersection(&pPlayer->GetRect(), &(*i)->GetRect()) zu ersetzen. Dann hast du sowohl die Gewissheit, dass die Kollisionsprüfung stimmt (da das ja eine bereitgestellte Funktion der SDL ist) als auch die Fallunterscheidung. Wobei beim ich beim Letzteren anhand deines Codebeispiel davon ausgehe, dass sich die Figur pro Frame wirklich nur in einer Achse bewegt. Sobald du ein komplexeres Spiel hast könnte diese Annahme problematisch werden.

Theoretisch könnten Rundungsfehler Probleme bei der Kollisionsüberprüfung machen. Ich glaube allerdings nicht, dass das hier (momentan) der Fall ist.

8

09.10.2014, 02:39

Hey Guten abend.
ich habe nun schweren Herzens meine Funktion durch die von SDL ersetzt. Ich mag einfach diese Vorgefertigten dinger nicht =)
wie auch immer ich komme aufs gleiche Ergebnis.

Allerdings habe ich mit ein wening rum probieren rausgefunden das es nur auftritt wenn der Player sich schräg bewegt.

Aufs Foto bezogen:
Bewege ich nun den Player, wie im Foto beschrieben, nach rechts unten (pfeiltaste Unten & Pfeiltaste Rechts gleichzeitig gedrückt) wird der Player in die Textur eingezogen und bewegt sich dann in alle Richtungen.

hast du vielleicht eine Ahnung?! :crazy:

MFG Urprimat :ninja:
»Urprimat« hat folgende Dateien angehängt:
  • 1.bmp (240,12 kB - 39 mal heruntergeladen - zuletzt: 21.06.2024, 21:50)
  • 2.bmp (481,08 kB - 46 mal heruntergeladen - zuletzt: 06.05.2024, 01:07)
Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.

Linus Torvalds

Lares

1x Contest-Sieger

  • Private Nachricht senden

9

09.10.2014, 09:55

Zitat


Dann hast du sowohl die Gewissheit, dass die Kollisionsprüfung stimmt (da das ja eine bereitgestellte Funktion der SDL ist) als auch die Fallunterscheidung. Wobei beim ich beim Letzteren anhand deines Codebeispiel davon ausgehe, dass sich die Figur pro Frame wirklich nur in einer Achse bewegt. Sobald du ein komplexeres Spiel hast könnte diese Annahme problematisch werden.


Dann ist es das Problem, was ich im vorherigen Post angedeutet habe:
Sieht dein Code noch ungefähr so aus?

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 if (SDL_HasIntersection(&pPlayer->GetRect(), &(*i)->GetRect())) //Funktion weiter unten
        {
            if (OldRect.x == pPlayer->GetRenderRect().x) //Falls es kein unterschied zwischen der alten & neuen X-Position gibt, muss der Spieler von Oben oder Unten kommen.
            {
             // ...
            }


            else if (OldRect.y == pPlayer->GetRenderRect().y)//Falls es kein unterschied zwischen der alten & neuen Y-Position gibt, muss der Spieler von Links oder rechts kommen.
            {
             // ...
            }

        }


Dann darfst du nicht schräg laufen. Zur Verdeutlichung ein Beispiel:
Deine Spielfigur befindet sich auf der Position (5/3). Pro Frame bewegt sich sich je einen Pixel auf beiden Achsen weiter.
D.h. im nächsten Frame wäre ihre Position (6/4).
Sowohl if(OldRect.x == pPlayer->GetRenderRect().x), als auch if(OldRect.y == pPlayer->GetRenderRect().y) sind dadurch false und es wird keine Korrektur der Position durchgeführt.

Das kann man lösen, indem man zwischen der Position aus dem letzten Frame und der Position des Kollisionsobjekts (also dem Block) eine Strecke berechnet und die Steigung dieser Strecke überprüft. Gibt bestimmt noch andere Varianten, aber das ist die erste die mir gerade einfällt.

10

09.10.2014, 14:18

Hey Guten Morgen =)

Ja immoment sieht mein Code genau so aus.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 if (SDL_HasIntersection(&pPlayer->GetRect(), &(*i)->GetRect())) //Funktion weiter unten
        {
            if (OldRect.x == pPlayer->GetRenderRect().x) //Falls es kein unterschied zwischen der alten & neuen X-Position gibt, muss der Spieler von Oben oder Unten kommen.
            {
             // ...
            }


            else if (OldRect.y == pPlayer->GetRenderRect().y)//Falls es kein unterschied zwischen der alten & neuen Y-Position gibt, muss der Spieler von Links oder rechts kommen.
            {
             // ...
            }

        }


Ich habe jedoch ein paar Verständnisprobleme in bezug auf die Strecke.
meinst du ich soll ein Steigunsdreieck berechnen? (so wie auf dem Bild)
und dann die Blaue Strecke als Differenz benutzen und mit dieser Differenz den Player wieder aus dem Objekt stoßen?!
oder was soll ich mit der Strecke anfangen?

Sorry ist mein erstes größeres Projekt, vorallem Spielprojekt und ich möchte deine Geduld nicht strapazieren... :thinking:


Mfg Urprimat :ninja:
»Urprimat« hat folgende Datei angehängt:
  • Unbenannt.bmp (481,08 kB - 36 mal heruntergeladen - zuletzt: 01.06.2024, 11:18)
Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.

Linus Torvalds

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Urprimat« (09.10.2014, 14:25)


Werbeanzeige