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

05.06.2017, 11:38

Listen Element in C++ kann nicht korrekt gelöscht werden

Seit nunmehr 2 Tagen versuche ich ein recht fieses Problem aus der (also meinem Code) zu schaffen. Ich verwende die SFML um meine Grafiken auf den Bildschirm zu zaubern, und alles funktioniert, doch beim löschen der Listenelemente (asteroid, schuss) bekomme ich nur eine Fehlermeldung ausgespuckt.
Lange rede kurzer Sinn - hier ist mein Code:

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
void cCollisionManager::checkCollision(cPlayer * pPlayer, std::list<cAsteroid*> *asteroidList, std::list<cShot*> *ShotList)
{

    sf::FloatRect PlayerBox = pPlayer->getSprite()->getGlobalBounds();
    auto it = asteroidList->begin();
    auto es = ShotList->begin();


    for (it; it != asteroidList->end();it++) {
        for (es; es != ShotList->end();es++) {

            sf::FloatRect asteroidboundingBox = (*it)->getSprite()->getGlobalBounds();
            sf::FloatRect ShotBox = (*es)->getSprite().getGlobalBounds();   
                                                                            
                                                                            
            if (asteroidboundingBox.intersects(ShotBox)) {                  
                asteroidList->erase(it);                                    
                ShotList->erase(es);                                        
                                                                            
                std::cout << "Asteroid destroyed" << std::endl;             
                *pPlayer->pPunkte += 1;                                     
                std::cout << *pPlayer->pPunkte << std::endl;
            }

            if (asteroidboundingBox.intersects(PlayerBox)) {
                if (*pPlayer->phealth >= 0.f)
                    *pPlayer->phealth -= 0.5f;
            }
        }
    }
}


Um mein Problem noch etwas näher zu beschreiben: Diese Funktion ist teil eines ganzen größeren und wird in der SFML Hauptschleife in jedem Durchlauf aufgerufen. In dieser Version meiner Kollisionsabfrage wird jedoch der innere If-Block gar nicht aufgerufen.
Vielleicht bin ich gerade auch nur blind für den Fehler^^, sowas soll schon mal vorkommen.

Zurück zum Thema: Verschiebe ich die beiden erase Funktionen jetzt aus dem if Block, damit sie auch JA aufgerufen werden, dann crasht das "Spiel" nur mit einer Fehlermeldung - die da wäre:

Zitat

DEBUG Assertion Failed! Expression: list iterator not incementable


Auf Stackoverflow hab ich auch schon mal gefragt aber die konnten nur auf ein bereits bestehendes Topic verweisen, das mir jetzt aber auch nicht helfen konnte!

Danke im Voraus

EDIT

Ich hab hier noch ein Bild von dem Fehler, und ich denke an dem lässt sich mein Problem auch ganz klar erkennen :D

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Fighter00HD« (05.06.2017, 11:49)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

05.06.2017, 11:53

Der Fehler ist eindeutig. Du iterierst in deiner Schleife mit einem Iterator, löschst aber ein Element. Damit wird der Iterator ungültig (Ist klar, denn er 'zeigt' ja auf ein gelöschtes Element. Was genau soll '++' da nun als Ergebnis liefern?) und dann knallt es. Schau dir mal an, was 'erase' zurück liefert.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

3

05.06.2017, 12:03

Erase liefert ja den vorherigen Iterator zurück. Und das kann man für gewöhnlich ja mit

C-/C++-Quelltext

1
it=erase(it)
erreichen

Aber bei mir ändert sich an der Fehlermeldung dennoch nichts :pinch:

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

05.06.2017, 12:12

Was glaubst du passiert bei deinem For-Loop, falls du das letzte Element der Liste entfernst (dein Iterator somit auf end() zeigt) und du dann '++' im Kopf der For-Schleife darauf ausführst? Du hast da also nicht nur ein Problem in deiner Konstruktion, sondern zwei. Eins ist gelöst, das andere nicht.

Ich kann mir übrigens nicht vorstellen, dass der Stackoverflow-Link das nicht wunderbar beschrieben und mit einem richtigen Beispiel demonstriert hat.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

5

05.06.2017, 12:26

Meine urpsrüngliche Idee sah etwas anders aus

Dieser ominöse StackOvflow Link besagt, das das Problem durch den Syntax
asteroidList->erase(it++)
beseitigt sein sollte. Wie gehabt schlagen die als Lösung auch eine while Schleife vor, die aber bei mir wegen der Implementierung in die SFML Schleife nicht funktioniert, da das Programm da dann einfach "hängen bleibt"

EDITUrsprünglich habe ich das ganze sowieso im C++11 Stil gelöst, wobei gelöst eben dieses Problem beinhaltet

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
for (auto it : *asteroidList) {
        for (auto es : *ShotList) {

            sf::FloatRect asteroidboundingBox = (it)->getSprite()->getGlobalBounds();
            sf::FloatRect ShotBox = (es)->getSprite().getGlobalBounds();    
                                                                    
            if (asteroidboundingBox.intersects(ShotBox)) {                  
                asteroidList->remove(it);
                ShotList->remove(es);

                std::cout << "Asteroid destroyed" << std::endl;             
                *pPlayer->pPunkte += 1;                                     
                std::cout << *pPlayer->pPunkte << std::endl;

                
                    
            }
            if (asteroidboundingBox.intersects(PlayerBox)) {
                if (*pPlayer->phealth >= 0.f)
                    *pPlayer->phealth -= 0.5f;
            }
                
        }
    }

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Fighter00HD« (05.06.2017, 12:34)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

6

05.06.2017, 12:31

Also zunächst mal gibt es keine "SFML Schleife". Wenn dein While hängen bleibt, hast du vergessen irgendwo den Iterator zu inkrementieren und somit geht das schon, wenn man es richtig macht. Und zweitens ist die Umformung in eine While-Schleife genau das, was ich eigentlich implizieren wollte, als ich dir mitgeteilt habe, dass deine For-Variante ein Problem hat. Geht sicher auch mit For, ist dann nur saumäßig hässlich.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

7

05.06.2017, 12:31

Zerlegt mal die For loops. Sprich schreib sie mal als while aus und dann schau dir nochmal BlueCobolds Aussage an (mit dem end).
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

8

05.06.2017, 12:46

Also sieht der ganze Code mit einer while Schleife so aus:

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
void cCollisionManager::checkCollision(cPlayer * pPlayer, std::list<cAsteroid*> *asteroidList, std::list<cShot*> *ShotList)
{

    sf::FloatRect PlayerBox = pPlayer->getSprite()->getGlobalBounds();
    
    auto it = asteroidList->begin();
    auto es = ShotList->begin();


    while (it != asteroidList->end()) {
        sf::FloatRect asteroidboundingBox = (*it)->getSprite()->getGlobalBounds();
        while (es != ShotList->end()) {

            sf::FloatRect ShotBox = (*es)->getSprite().getGlobalBounds();

            
            if (asteroidboundingBox.intersects(ShotBox)) {
                it = asteroidList->erase(it);
                es = ShotList->erase(es);

                std::cout << "Asteroid destroyed" << std::endl;
                *pPlayer->pPunkte += 1;
                std::cout << *pPlayer->pPunkte << std::endl;
            }
            
            if (asteroidboundingBox.intersects(PlayerBox)) {
                if (*pPlayer->phealth >= 0.f)
                    *pPlayer->phealth -= 0.5f;
            }
            es++;
        }
        it++;
        
    }
}


Wenn dein While hängen bleibt, hast du vergessen irgendwo den Iterator zu inkrementieren.


Am Ende habe ich jetzt jeweils einmal es und it wieder um eins erhöht. So bleibt die Schleife zumindest nicht stecken. Danke dafür. Jetzt bleibt aber noch ein Kernproblem, dass einfach gar nichts passiert wenn ein Schuss einen Asteroiden trifft.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

9

05.06.2017, 13:00

Die Schleifen sind aber noch immer nicht ganz korrekt. Siehe Kommentar #4. Du führst auf dem Iterator immer ein "++" aus, selbst dann, wenn er bereits auf "end()" zeigt, was zu Problemen führt. Außerdem vergisst du innerhalb der ersten Schleife den Iterator 'es' neu zu starten und gehst somit nach dem ersten Asteroiden überhaupt gar keine Schüsse mehr durch. Das hätte dir bei der Verwendung des Debuggers eigentlich auffallen müssen, dass die innere Schleife beim zweiten Asteroiden gar nicht mehr betreten wird.


Ich würde dir übrigens raten von std::list zu std::vector zu wechseln und frage mich, was mit dem cAsteroid* passiert, wenn du ihn aus der Liste wirfst. Wird der korrekt gelöscht? Dazu wäre vielleicht ein Blick auf std::unique_ptr eine gute Sache.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »BlueCobold« (05.06.2017, 13:05)


10

05.06.2017, 13:04

Eine If Abfrage als Lösung?

Die Schleifen sind aber noch immer nicht ganz korrekt. Siehe Kommentar #4. Du führst auf dem Iterator immer ein "++" aus, selbst dann, wenn er bereits auf "end()" zeigt, was zu Problemen führt.


Das riecht ja förmlich nach einer if Abfrage - ist übrigens mein erstes "Spiel" deswegen bringt mich das eben auch etwas an meine Grenzen :dash:

Ich hätte da an sowas gedacht

C-/C++-Quelltext

1
2
if(it != asteroidList->end())
            it++;

Werbeanzeige