java.util.ConcurrentModificationException problem in der Logik? Quaxli-Tutorial

Status
Nicht offen für weitere Antworten.

Keylan

Mitglied
Hallo zusammen,

wie so viele andere auch, wollte ich meinen Einstieg in die Javaprogrammierung endlich mal mit vollständigen Projekten füllen, statt nur hier oder da eine Klasse in bestehende Projekte zu ergänzen.

Damit die Motivation stimmt wird also ein kleines Spiel programmiert, habe mich dafür sehr dich an das 2D-Spieletutorial von Quaxli gehalten (vielen Dank an dieser Stelle für das tolle Tutorial).
Natürlich wollte ich es nicht 1:1 nachbauen, neben anderen Grafiken bekommen die Objekte bei mir also auch andere Logiken, und es sollen später auch etwas mehr öbjekte hinzukommen.

Noch habe ich auf Dynamische Bewegungen verzichtet, nach dem Motto erstmal das grobe Spiel erschaffen, und dann verfeinern.

Im TuT bin ich jezt ca. auf Seite 52 also noch vor Kooalisionsabfragen, und Objektentrümpelung.

Wenn ich das Spiel zum Laufen lasse klappt soweit alles wie gewünscht, nur kommt nach relativ kurzer Zeit (zwischen 10 sec. und 8 min) eine Exception und das Spiel friert ein:
Code:
Exception in thread "Thread-4" java.util.ConcurrentModificationException
	at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
	at java.util.AbstractList$Itr.next(Unknown Source)
	at gamepack.GamePanel.doLogic(GamePanel.java:166)
	at gamepack.GamePanel.run(GamePanel.java:135)
	at java.lang.Thread.run(Unknown Source)

das Ganze verweist nun zum einen Auf die doLogic() Methode und dann eben auf deren Aufruf

[JAVA=165] private void doLogic(){
for(Movable mov:actors){
mov.doLogic(delta);
}
}[/code]

[JAVA=128] @Override
public void run() {
while(game_running){

computeDelta();
if(isStarted()){
checkKeys();
doLogic();
moveObjects();
}
repaint();

try{
Thread.sleep(10);
} catch(InterruptedException e) {}
}
}[/code]

Wirklich viel anfagen kann ich damit nicht vor allem habe ich überhaupt nicht verstanden warum und wie das Interfave Movable mit long-Variablen arbeitet.:bahnhof:

Nun gut ich vermute das Problem in den doLogic() Methoden meiner Objekte, was dort allerdings schief läuft kann ich mir auch nach mehrmaligen überprüfen nicht erklären.

Hier mal der Code:

Aufrufe aus der main GamePanel
[JAVA=63] private void doInitializations(){

backround = loadPics("gamepack/pics/backround2.jpg",1)[0];

last = System.nanoTime();

actors = new Vector<Sprite>();
pi = this.loadPics("gamepack/pics/Point2.gif",12);
for(int k = 1;k<10;k++){

}
BufferedImage[] player = this.loadPics("gamepack/pics/Blue4.gif",5);
beams = loadPics("gamepack/pics/Beam.gif",8);
orb = new Player(player,500,375,100,this);

actors.add(orb);

timerW = new Timer(800,this);
timerW.start();
timerP = new Timer(2000,this);
timerP.start();

if(!once){
once = true;
Thread t = new Thread(this);
t.start();
}
}
private void createHPoints(BufferedImage[] p){

int x;
if(1<Math.random()*2){x=-20;}else{x = getWidth();}
int y = (int) (Math.random()*getHeight());
Point point = new Point(p,x,y,250,this);
actors.add(point);

}
private void createVPoints(BufferedImage[] p){
int y;
if(1<Math.random()*2){y=-20;}else{y = getWidth();}
int x = (int) (Math.random()*getHeight());
Point point = new Point(p,x,y,250,this);
actors.add(point);
}

private void createBeam(){
int x = 0;
int y = (int) (Math.random()*getHeight());
int height = (int) (Math.random()*2);

if(height<1){
x = -beams[0].getWidth();
}else{
x = getWidth();
}
Beam beamObject = new Beam(beams,x,y,100,this);
beamObject.setVerticalSpeed(0);
if(x>0){
beamObject.setHorizontalSpeed(-600);
}else{
beamObject.setHorizontalSpeed(600);
}
actors.add(beamObject);
}[/code]

und hier die Objektklassen

Spielfigur Orb
Java:
package gamepack;

import java.awt.image.BufferedImage;

public class Player extends Sprite{
	
	public Player(BufferedImage[] i, double x, double y, long delay, GamePanel p){
		super(i,x,y,delay,p);
	}
	@Override
 	public void doLogic(long delta){
		super.doLogic(delta);
			
			if(getX()<0){
				setHorizontalSpeed(50);
			//	setX(0);
			}
			if(getX()+getWidth()>parent.getWidth()){
				setHorizontalSpeed(-50);
			//	setX(parent.getWidth()- getWidth());
			}
			
			if(getY()<0){
				setVerticalSpeed(50);
			//	setY(0);
			}
			if(getY()+getHeight()>parent.getHeight()){
				setVerticalSpeed(-50);
			//	setX(parent.getHeight()-getHeight());
			}
		}
	}

Punkte zum einsammeln
Java:
package gamepack;

import java.awt.image.BufferedImage;

public class Point extends Sprite{

	final int SPEED = 100;
	
	public Point(BufferedImage[] i, double x, double y, long delay, GamePanel p){
		super(i,x,y,delay,p);
	}
		
	@Override
	public void doLogic(long delta){
		super.doLogic(delta);
		
		if(getCenterX()<parent.orb.getCenterX()){
			setHorizontalSpeed(SPEED);
		//	setX(0);
		}
		if(getCenterX()>parent.orb.getCenterX()){
			setHorizontalSpeed(-SPEED);
		//	setX(parent.getWidth()- getWidth());
		}/*else{
			setHorizontalSpeed(0);
			}*/
		
		if(getCenterY()<parent.orb.getCenterY()){
			setVerticalSpeed(SPEED);
		//	setY(0);
		}
		if(getCenterY()>parent.orb.getCenterY()){
			setVerticalSpeed(-SPEED);
		//	setX(parent.getHeight()-getHeight());
		}/*else{
			setVerticalSpeed(0);
		}*/
	}
}

und die erste Gegnerklasse Strahlenschüsse
Java:
package gamepack;

import java.awt.image.BufferedImage;

public class Beam extends Sprite {
	
	public Beam(BufferedImage[] i,double x, double y, long delay, GamePanel p){
		super(i,x,y,delay,p);
	}
	@Override
	public void doLogic(long delta){
		super.doLogic(delta);
	}
	
	public void setHorizontalSpeed(double d){
		super.setHorizontalSpeed(d);
		
		if(getHorizontalSpeed()<0){
			setLoop(4,7);
		}else{
			setLoop(0,3);
		}
	}

}

Die Klasse Sprite (abstrakte überklasse aller Spielobjekte) und die Interfaces sind unverändert aus dem Tutorial übernommen.

Ja ich weis alles was nicht direkt von Quaxli übernommen ist ist gleich etwas unsauber, aber wie gesagt ich steige noch ein und lerne dazu. Gehe davon aus auch wenn alles läuft werde ich den code noch mehrfach überarbeiten.

Bevor ich mich nun daran setzen wollte die nächsten beiden GegnerKlassen zu schreiben wollte ich auf jeden fall den Bug rausbekommen.

Kann mir vieleicht jemand helfen? Suche ich überhaupt an der richtigen Stelle?

Vielen Dank schonmal für jeden Hilfeversuch
 
Die Exception tritt bei for-each Schleifen auf.
Ich versuche die immer zu vermeiden, auch wenn sie schön aussehen 😉
Wenn du über einen index iterierst, sollte das nicht mehr auftreten.

Ich kenne den Quelltext *g* habe den auch mal verwendet.
Naja das Problem liegt also hier:
Java:
    private void doLogic(){
        for(Movable mov:actors){
            mov.doLogic(delta);
        }
    }

Mach's doch so:
for-Schleife ändern, sodass du bequem über den Index iterieren kannst:
Java:
    private void doLogic(){
        for(int i=0;i<actors.size();i++){
            actors.get(i).doLogic(delta);
        }
    }

Geht aber hier nur, da in deinem Vector alle Objekte die doLogic Methode enthalten.
 
Zuletzt bearbeitet:
über Vector und ArrayList läßt sich identisch iterieren,
und eine for-Schleife zu empfehlen, die auf unsaubere Weise bestimmte Missstände verdeckt, halte ich für gefährlich

leider habe ich im Moment nicht die Muße, dieses riesige Programm näher anzuschauen,
vielleicht später falls dann noch aktuell (Rückmeldung) und nicht schon wer anders alles auflöst
 
Dachte immer Vector arbeitet nich mit Indizes ^^

Bei der for Schleife ist eigentlich noch dazuzusagen, dass man da aufpassen muss, ja.
Die ursprüngliche Schleife hat nur über die Objekte iteriert, die Moveable sind.
Da die Elemente des Vectors aber alle Sprites sind und diese Moveable implementieren,
sollte das an dieser Stelle keinen Unterschied machen, da alle die doLogic Funktion besitzen.

Aber generell kann man das halt nicht so umwandeln, das ist wahr.
 
Zu blöd das die Suchfunktion des Forums die Exception als Suchbegriff nicht zulässt (ignoriert), sonst hätte ich den Link von Marco wohl auch selbst gefunden.

Habe das Problem jetzt zwar noch nicht gelöst, aber erstma wieder einige hinweise wo ich das Problem suchen muss.

Werde mir dann nachher mal die threadsicheren Collections anschaun, und das mit dem AntiAlising war auch gleich interessant, wenn auch sicher eher ein goodie für den Abschluss des Projektes.
 
Super

einfach Vector gegen CopyOnWriteArrayList ersetzen und alles läuft prima. Fast schade das es so eine Einfache lösung bereits gibt, warscheinlich werde ich so über den eigentlichen Kern des Problems nicht so viel lernen als hätte ich es elementar lösen müssen.

Bei mehreren Threads kann ich mir das ja noch erklären, aber habe ich hier meherere Threads? Ich dachte ich hätte nur einen, udn warum dieser dann ein dynamisches Objekt nicht handlen kann will ich noch nicht ganz verstehen.
 
Soo inzwischen habe ich animationen etwas überarbeitet und dabei noch eine ganze Menge von den Zeichenmethoden und der Loop gelernt die nachher die Animation auswählt.

Allerdings ist mir dann aufgefallen das sich eine meiner Objektklassen nicht verhält wie sie soll.

Passt zwar nichtmehr zur Überschrift, aber ich wollte jetzt für eine kleinigkeit kein neues Thema erstellen.

Also, ich habe eine Gegnerklasse die bei Erzeugung auf mein Spieler-Objekt zufliegt, Dabei aber nicht nachträglich Korrigiert, so das dieser Ausweichen kann. Also eine Klassische Schuss Funktion würde ich sagen.

Da Bewegung und Verhalten (ohne Koalision) nur einmal bestimmt werden müssen mache ich alle Berechnungen in der aufrufenden Methode

[JAVA=91] private void createHShots(BufferedImage[] p){

int x;
if(1<Math.random()*2){x=-20;}else{x = getWidth();}
int y = (int) (Math.random()*getHeight());
Shot shot = new Shot(p,x,y,200,this);
int h = (int) (orb.getCenterX()-shot.getCenterX());
int w = (int) (orb.getCenterY()-shot.getCenterY());
shot.setHorizontalSpeed((int)((h/Math.sqrt((w*w)+(h*h)))*shotSpeed));
shot.setVerticalSpeed((int)((w/Math.sqrt((w*w)+(h*h)))*shotSpeed));
actors.add(shot);

}[/code]

orb ist dabei das Spielerobjekt das leider nicht so gut getroffen wird wie ich hoffte, wenn es unbewegt ist.

ca. 20% der Schüsse geht deutlich daneben, kleine Abweichungen kann ich aufgrund der Wurzel und des casts' nachvollziehen aber nicht so heftige.

Dazu Variiert auch die Geschwindigkeit der Schüsse sehr was auch auf fehler in der Berechnung hinweist.
 
Ganz banal würd' ich "doLogic()" so implementieren:
Java:
private void doLogic() {
  List<Movable> tmp = (List<Movable>) actors.clone();
  Iterator<Movable> i = tmp.iterator();
  while(i.hasNext()) {
    i.next().doLogic(delta);
  }
}
Das Klonen der Liste ist notwendig, wenn andere Threads während der "while"-Schleife die Liste, ob ArrayList oder Vector ist dabei egal, modifizieren. Innerhalb der "while"-Schleife (also in der "doLogic(delta)"-Methode darf die Liste ebenfalls nicht verändert werden. Modifizieren bzw. verändern der Liste bezieht sich dabei ausschliesslich auf Zugriffe, die ihre Grösse ändert. Zuletzt: Auf diese Art kann auf threadsichere Collections auch verzichtet werden. Hauptsache ist, sie implementieren "Cloneable".
 
Zuletzt bearbeitet von einem Moderator:
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben