Exception beim löschen verhindern ?

01/11/2015 14:12 xEncounter#1
Code:
import java.util.ArrayList;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

public class Cardrive extends BasicGame {

	private Image bg; // Attribut für das Hintergrundbild
	private ArrayList<Car> autos = new ArrayList<Car>(); 

															
	private Player player; // Der Spieler

	private int neuesAutoTimer = 0;
	private boolean gameOver = false; // Die GameOver Variable
	private Input input; // Spieler Input

	@Override
	public void init(GameContainer gc) throws SlickException {
		bg = new Image("bg.png"); 
		player = new Player(gc.getInput()); 
		input = gc.getInput();
	}

	@Override
	public void update(GameContainer gc, int delta) throws SlickException {

		neuesAutoTimer += delta;

		player.bewegen(delta);

		if (neuesAutoTimer >= 750 && gameOver == false) {

			autos.add(new Car((int) (Math.random() * (2 - 0) + 0)));

			neuesAutoTimer = 0;

		}

		for (Car auto_now : autos) {
			auto_now.bewegen(delta);
		}

		if (autos.size() > 0 ) {

			for (Car auto_now : autos) {
				if (player.getHitbox().intersects(auto_now.getHitbox())) {

					autos.remove(auto_now);

					gameOver = true;

				}
			}
		}

		if (input.isKeyPressed(Input.KEY_ENTER) && gameOver == true) {

			autos.clear();
			neuesAutoTimer = 0;
			gameOver = false;

		}

	}

	@Override
	public void render(GameContainer gc, Graphics gfx) throws SlickException {

		// Der Hintergrund wird gezeichnet
		bg.draw();

		// Alle Autos in der Liste werden gezeichnet
		for (int i = 0; i < autos.size(); i++) {
			Car auto = autos.get(i);
			auto.zeichnen();
			gfx.draw(auto.getHitbox());
		}

		// Den Spieler mit der Hitbox zeichen
		player.zeichnen();
		gfx.draw(player.getHitbox());

		// Anzeige für den momentanen delta-Wert
		gfx.drawString("delta: " + neuesAutoTimer, 25, 50);

		// Anzeige für die momentanen Autos auf dem Bildschirm
		gfx.drawString("Autos: " + autos.size(), 25, 75);

		// Gameover anzeigen
		if (gameOver) {
			gfx.setColor(Color.red);
			gfx.drawString(
					"Du hast verloren!! \nDrücke Enter für einen Neustart!",
					200, 300);
		} else {
			gfx.setColor(Color.white);
		}
	}

	public Cardrive(String title) {
		super(title);

	}

	public static void main(String[] args) throws SlickException {
		AppGameContainer myGame = new AppGameContainer(
				new Cardrive("Car Drive"));
		myGame.setDisplayMode(600, 800, false);

		myGame.start();
	}

}
Irgendwo ist da ein Fehler in der Spielelogik...


Sobald man das erste Auto trifft, ist Game Over. Soweit so gut.

Dann hören die Autos auf zu spawnen(auch gut), dann läuft man absichtlich in das letzte Auto(auch noch okay), dabei crasht das Spiel mit einer Exception(nicht gut).

Hier die detaillierte Ausgabe:
Code:
Sun Jan 11 14:11:15 CET 2015 ERROR:null
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
	at java.util.ArrayList$Itr.next(Unknown Source)
	at Cardrive.update(Cardrive.java:64)
	at org.newdawn.slick.GameContainer.updateAndRender(GameContainer.java:663)
	at org.newdawn.slick.AppGameContainer.gameLoop(AppGameContainer.java:411)
	at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:321)
	at Cardrive.main(Cardrive.java:129)
Sun Jan 11 14:11:15 CET 2015 ERROR:Game.update() failure - check the game code.
org.newdawn.slick.SlickException: Game.update() failure - check the game code.
	at org.newdawn.slick.GameContainer.updateAndRender(GameContainer.java:669)
	at org.newdawn.slick.AppGameContainer.gameLoop(AppGameContainer.java:411)
	at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:321)
	at Cardrive.main(Cardrive.java:129)
01/11/2015 14:35 xXKonaXx#2
Benutze einen Iterator um durch die Objekte zu gehen.
[Only registered and activated users can see links. Click Here To Register...]
01/11/2015 14:48 xEncounter#3
Was genau ist ein Iterator und wie kann ich ihn implementieren ?

Beispiel gerne gesehen ^^
01/11/2015 14:58 dowhile#4
Was? [Only registered and activated users can see links. Click Here To Register...], Folie 20f

Wie?
Code:
Iterator<E> it = list.iterator();
while (it.hasNext()) { E obj = it.next(); }
01/11/2015 15:03 xEncounter#5
Also soweit ich den Code verstehe, geht dieser Iterator soweit alle Elemente in der Liste durch solange es ein nächstes Element hat ?

Kann man dieses Iterator zum jeweiligen Element "returnen" oder was ist die Aufgabe des Iterators ?
01/11/2015 15:08 SteMu82#6
Statt
Code:
for (Car auto_now : autos) {
	if (player.getHitbox().intersects(auto_now.getHitbox())) {
           autos.remove(auto_now);
           gameOver = true;
        }
}
Würdest Du schreiben:

Code:
Iterator<Car> it = autos.iterator();
while (it.hasNext()) {
   Car auto_now = it.next();
   if (player.getHitbox().intersects(auto_now.getHitbox())) {
       it.remove();
       gameOver = true;
   }
}
Siehe: [Only registered and activated users can see links. Click Here To Register...]
Der Unterschied ist, dass Du hier mit it.remove() ein Element aus der Liste löschen kannst, ohne dass eine ConcurrentModificationException geworfen wird.
ConcurrentModificationException bedeutet ja, dass mehrere gleichzeitige Operationen auf der Liste ausgeführt werden, die dazu führen können, dass das was passiert mehr oder weniger zufällig ist (jedenfalls nicht eindeutig definiert). In diesem Fall iterierst Du über alle Elemente der Liste, entfernst dabei allerdings Elemente. Jetzt ist nicht definiert, ob Du zum Beispiel ein Element entfernt hast, über das Du schon iteriert hast oder erst noch iterieren wirst, deshalb verwendet man dort dann einen Iterator, der nur das aktuelle Element entfernen kann.
Anzumerken ist auch, dass nicht alle Arten von Collections remove() bei einem Iterator unterstützen. Interessant ist manchmal auch ein ListIterator, je nach Einsatz.
01/11/2015 15:13 xEncounter#7
Das ist etwas, was ich verstehe.

Jetzt leuchtet mir die Funktion des Itertators auch ein.

Danke !

#closerequest
01/11/2015 15:17 SteMu82#8
Habe noch eine mehr oder weniger umgangssprachliche und vereinfachte Erklärung hinzugefügt. Viele wollen ja auch verstehen, was sie machen und warum :D
01/11/2015 15:36 xEncounter#9
Natürlich ist es mir wichtig zu verstehen wie man so etwas anwendet und wie so etwas funktioniert, nur wenn mir jemand Dokus aus irgendeiner Uni oder was weiß ich postet ist der Verstehen-Effekt = 0.

Daher danke ich dir für die Erklärung.
01/11/2015 16:46 XxharCs#10
Du musst aufpassen wenn du Iterator nach der oben beschriebenen Weise verwendest, und zwar falls man durch iterriert, befindet man sich dann an letzter Stelle. Leider liefert der Iterator aus der Java SDK kein zurückspringen auf die erste Stelle einer Liste/Map/whatever wie der Iterator zB aus dem MySql-Connector lib.

Bei jeder neuen durch iterrierung musst du wieder auf die erste Stelle gehen, leider musst du in diesem fall jetzt zurück iterrieren und das kostet Zeit.

Deswegen rate ich dir in einer for-Schleife durch zu iterrieren weil du da kein solches Problem haben würdest, da es nur in der for-Schleife gültig ist(der Iterator).

Code:
for(Iterator<Car> it = autos.iterator(); it.hasNext(); ) {
    Car auto_now = it.next();
    if (player.getHitbox().intersects(auto_now.getHitbox())) {
        it.remove();
        gameOver = true;
    }
}
01/12/2015 04:08 SteMu82#11
Sehe da jetzt ehrlich gesagt keinen Unterschied oder verstehe die Erklärung nicht.
Der Iterator wird ja nicht mehrfach verwendet und verliert ja davon abgesehen nach jedem Aufruf der "update"-Methode seine Gültigkeit.
autos.iterator() gibt bei jedem Aufruf einen neuen Iterator zurück (wäre auch schlimm wenn nicht) und wenn man wieder von vorne anfangen will, reicht es auch einfach sich damit einen neuen Iterator zu holen.

Oder meintest Du allgemein? Da gilt das natürlich, da bei jedem Aufruf von next() der Iterator auch eins weiter läuft. Allerdings habe ich noch nicht gesehen, dass jemand zwei Mal direkt hintereinander über die gleiche Liste iteriert und es Sinn machen würde den selben Iterator zu verwenden.

Wenn man dazu neigt solche komischen Dinge zu tun oder es einfach nicht weiß, ist die vorgeschlagene Alternative natürlich sinnvoll, weil sie es einfach garnicht ermöglicht ihn danach nochmal zu verwenden. Hatte ich persönlich so allerdings noch nie gesehen und auch noch keinen Fall, in dem es einen Unterschied machen würde.

Wenn ich also irgendwas übersehen habe, sag bitte bescheid. Lerne immer gerne dazu.