Allegro/STL: Element aus Liste löschen, Absturz
-
Hallo,
Ich versuche gerade einen Space Invaders Klon zu schreiben. Das klappt bisher auch wunderbar, nur dieses mal stehe ich vor einem Problem, das ich ohne Hilfe wohl nicht lösen kann.
Bei dem Spiel sind die gegnerischen Raumschiffe oben am Bildschirm formiert, der Spieler lenkt am unteren Rand ein Raumschiff hin und her und muss die Raumschiffe abschießen, bevor er getroffen wird.
Die Feinde bestehen aus einer Klasse, von der mehrere Instanzen in einem Container, mit Hilfe der STL-Funktion "list", angelegt sind. In der Klasse Feind befindet sich eine Funktion "Schuss", die aus einem Wertebereich eine Zufallszahl zurückgibt, während in der Spielschleife abgefragt wird, ob der Rückgabewert einem entsprechenden Wert entspricht. Tut er dies wird ein Listenelement (Klasse "Schuss") im "list"-Container angelegt. Ist das nicht der Fall, passiert nichts. Das funktioniert auch alles, die Gegner bewegen sich wie gewollt und schießen ab und zu.
Jetzt will ich aber nicht, dass der Container unnötig groß wird und habe vor die Listenelemente zu löschen, die nicht mehr benötigt werden, d.h., die aus dem Bildschirmbereich verschwinden, zu löschen. Dazu wollte ich den Befehl "erase()" verwenden. Hier mal das Problemkind://Gegner pro Zeiteinheit bewegen if (timerCounterEnemy ==0) { for (EnemyIterator itor = enemyList.begin(); itor!= enemyList.end(); itor++) { enemy =*itor; enemy->update(); //GEGNER-Schuss initialisieren if (enemy->rndShooting()==ENEMY_SHOOTING) { shot_enemy = new CShot_ENEMY(shotsprplayer, PLAYER_WIDTH, PLAYER_HEIGHT); shot_enemy->setPosition((int)enemy->getX(), (int)enemy->getY()); shotListEnemy.push_back(shot_enemy); } //GEGNER-Schuss löschen for (ShotIteratorEnemy itor = shotListEnemy.begin(); itor!= shotListEnemy.end(); itor++) { if ((int)shot_enemy->getY() >= SCREEN_HEIGHT) { shotListEnemy.erase(itor); } } } timerCounterEnemy--; } //Gegner auf Bitmap zeichnen for (EnemyIterator itor = enemyList.begin(); itor!= enemyList.end(); itor++) { enemy =*itor; enemy->draw(doubleBuffer); } //GEGNER-Schüsse darstellen for (ShotIteratorEnemy itor = shotListEnemy.begin(); itor!= shotListEnemy.end(); itor++) { if (shot_enemy->getY()<=SCREEN_HEIGHT) { shot_enemy =*itor; shot_enemy->move(); shot_enemy->draw(doubleBuffer); } }Ich habe mal den ganzen Aufbau der "Gegnerverwaltung" kopiert, damit die Struktur besser klar wird. Das Problem liegt denke ich nicht bei "//GEGNER-Schuss löschen", sondern bei "//GEGNER-Schüsse darstellen". Wenn der Schuss gelöscht wird stürzt das Programm kurz darauf ab und gibt folgende Fehlermeldung aus:
*Shutting down Allegro due to signal #11
Segmentation fault (core dumped)
*Das kam bisher immer nur, wenn irgendetwas mit dem Blitten nicht geklappt hat.
Ich habe allerdings keine Ahnung warum das nicht funktioniert. Ich denke mir, dass das daran liegen könnte, dass das Listenelement zwar gelöscht wird, die "//GEGNER-Schüsse darstellen"-Funktion jedoch zu schnell aufgerufen wird und versucht das nicht mehr vorhandene Listenelement zu blitten. Also die Befehle "Löschen" und "Darstellen" zu Nahe beeinander liegen. Andererseits kann ich mir das dann doch nicht so recht vorstellen, zumal ich ja eine Bedingung eingebaut habe, damit Schüsse, außerhalb des Bildschirmbereiches, nicht mitgeblittet werden.
Ich danke schonmal im Voraus

-
Ok, ich habe mal das Löschen der Schüsse aus der Schleife herausgenommen (weiß selber nicht mehr, was sie dort zu tun hatte. Ich sollte mal strukturierter vorgehen) und verändert:
//GEGNER-Schuss löschen for (ShotIteratorEnemy itor = shotListEnemy.begin(); itor!= shotListEnemy.end(); itor++) { if ((int)shot_enemy->getY() == SCREEN_HEIGHT) { shot_enemy =*itor; shotListEnemy.erase(itor); } }Aber es funktioniert immernoch nicht

So, ich habe sie nochmal umgeschrieben, so dass das Löschen und das Blitten in einer Schleife ist:
//GEGNER-Schuss darstellen/löschen for (ShotIteratorEnemy itor = shotListEnemy.begin(); itor!= shotListEnemy.end(); itor++) { shot_enemy =*itor; //GEGNER-Schüsse darstellen if ((int)shot_enemy->getY()<= 200) { shot_enemy->move(); shot_enemy->draw(doubleBuffer); } //Gegner-Schüsse löschen if ((int)shot_enemy->getY() > SCREEN_HEIGHT+20) { shotListEnemy.erase(itor); } }Was ich aber überhaupt nicht verstehe ist, dass wenn "shot_enemy->getY()<=200" der Schuss immernoch dargestellt wird. Ich denke, dass darin der Fehler liegt.
Edit: Mir ist bewusst, dass der Schuss sich dann ab 200 nicht mehr bewegen würde und der Fall "(int)shot_enemy->getY() > SCREEN_HEIGHT+20)" nie einträte, aber ich wollte eben mal testen, ob die Bedingung überhaupt akzeptiert wird, was nie leider nicht wird.
Könnt ihr mir helfen?
Edit2: Das war vielleicht ein Schuss in den Ofen ^^ Ich habe die falsche .cpp kompiliert.

Jetzt läuft das ganze mit den Bedingungen. Allerdings ist das Problem mit dem Löschen der Listenelementen immernoch aktuell. Fehlermeldung: "Aborted (core dumped)"
-
norm4n schrieb:
Ok, ich habe mal das Löschen der Schüsse aus der Schleife herausgenommen (weiß selber nicht mehr, was sie dort zu tun hatte. Ich sollte mal strukturierter vorgehen) und verändert:
//GEGNER-Schuss löschen for (ShotIteratorEnemy itor = shotListEnemy.begin(); itor!= shotListEnemy.end(); itor++) { if ((int)shot_enemy->getY() == SCREEN_HEIGHT) { shot_enemy =*itor; shotListEnemy.erase(itor); } }Aber es funktioniert immernoch nicht

In der if()-Abfrage zeigt shot_enemy auch nicht auf das gerade bearbeitete Listenelement, sondern auf das zuletzt eingefügte. Und außerdem macht list::erase() den Iterator ungültig, auf den es verweist (und liefert dessen Nachfolger zurück):
for(ShotIterator pos = shotListEnemy.begin();pos!=shotListEnemy.end();) { if(pos->GetX() >= SCREEN_HEIGHT) pos = shotListEnemy.erase(pos);//oder shotListEnemy.erase(pos++); else ++pos; }(PS: Wozu sind denn die ganzen Casts nach int notwendig?)
-
Hallo,
Danke für die Antwort. Die Casts kann ich rauswerfen, hast Recht. Der Code funktioniert auch, allerdings nicht so wie du ihn geschrieben hast.
for(ShotIterator pos = shotListEnemy.begin();pos!=shotListEnemy.end();) { if(pos->getY() >= SCREEN_HEIGHT) pos = shotListEnemy.erase(pos);//oder shotListEnemy.erase(pos++); else ++pos; }Bringt die Fehlermeldung:
Fehler: Abfrage des Elementes »getY« in »* pos. std::_List_iterator<_Tp>::operator-> [with _Tp = CShot_ENEMY*]()«, das vom Nicht-Klassentyp »CShot_ENEMY*« ist
mit "pos->shot_enemy->getY()" kommt das gleiche. "shot_enemy->getY()" geht zwar, allerdings wird dann die ganze Liste gelöscht. Warum ist mir klar, aber wie ich das Problem beheben kann, ist mir noch nicht bewusst.
-
Wenn du in der Liste Zeiger stehen hast, mußt du die Iteratoren doppelt dereferenzieren (also
(*pos)->getY()).
-
Danke
