(Java/LWJGL) ConcurrentModificationException with only one thread
2 replies, posted
I've been getting to grips with LWJGL by making a space invaders clone. So far it's gone well until I added bullets, which check if they intersect any of the invaders and if they do, remove themselves and the invader. For some reason, I get a ConcurrentModificationException (I don't really know what that means but from what I understand it's when two threads try to access an arraylist at the same time). I only have one thread as far as I know (I've not made any new ones) so how can this be possible?
Here is the code:
Stack trace:
[code]
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at spaceinvaders.Player.update(Player.java:35)
at spaceinvaders.Main.update(Main.java:85)
at spaceinvaders.Main.start(Main.java:52)
at spaceinvaders.Main.main(Main.java:154)
[/code]
Main class:
[lua]
package spaceinvaders;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.input.Mouse;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
public class Main
{
public static final int DISPLAY_WIDTH=480,DISPLAY_HEIGHT=272,X_BOUND=10;
static Player player = new Player();
private static List<Invader> invaders = new ArrayList<Invader>();
long lastFrame;
int fps;
long lastFPS,lastMove;
public void start()
{
invaders.add(new InvaderStandard(100,100));
invaders.add(new InvaderStandard(200,100));
invaders.add(new InvaderStandard(300,100));
try
{
Display.setDisplayMode(new DisplayMode(DISPLAY_WIDTH,DISPLAY_HEIGHT));
Display.create();
}
catch(LWJGLException e)
{
e.printStackTrace();
System.exit(0);
}
Mouse.setGrabbed(true);
initGL();
getDelta();
lastFPS = getTime();
lastMove= getTime();
player.lastBullet = getTime();
while(!Display.isCloseRequested())
{
int delta = getDelta();
update(delta);
renderGL();
Display.update();
Display.sync(60);
if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
{
System.exit(0);
}
}
Display.destroy();
}
public void update(int delta)
{
float deltaUpdate=Invader.getDeltaUpdate();
if(getTime()-lastMove>=deltaUpdate)
{
if(!Invader.moveDown())
{
for(Invader i : invaders)
{
i.update();
}
}
else
{
Invader.updateY();
}
lastMove+=deltaUpdate;
}
player.update(delta);
updateFPS();
}
public void initGL()
{
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
public void renderGL()
{
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glColor3f(255, 0, 0);
GL11.glPushMatrix();
for(Invader i : invaders)
{
if(i instanceof InvaderStandard)
{
i.render();
}
}
for(Bullet b : player.bullets)
{
b.render();
}
player.render();
GL11.glPopMatrix();
}
public static long getTime()
{
return (Sys.getTime()*1000)/Sys.getTimerResolution();
}
public int getDelta()
{
long time = getTime();
int delta = (int)(time-lastFrame);
lastFrame = time;
return delta;
}
public static List<Invader> getInvaders()
{
return invaders;
}
public void updateFPS()
{
if(getTime()-lastFPS>1000)
{
Display.setTitle("FPS: "+fps);
fps=0;
lastFPS+=1000;
}
fps++;
}
static Main prog;
public static void main(String[] args)
{
prog = new Main();
prog.start();
}
}
[/lua]
Invader Class:
[lua]
package spaceinvaders;
import org.lwjgl.opengl.GL11;
public abstract class Invader {
public final float WIDTH=30f,HEIGHT=30f;
private static float Y_INC=15f;
private static float deltaUpdate=500f;
private float x,y;
private static int DIRECTION=1;
private static float XSPEED=10f;
public void update()
{
x += XSPEED * DIRECTION;
}
public static void updateY()
{
Invader.DIRECTION*=-1;
Invader.deltaUpdate-=50f;
for(Invader i : Main.getInvaders())
{
i.y+=Y_INC;
}
}
public void render()
{
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex2f(x,y);
GL11.glVertex2f(x+WIDTH, y);
GL11.glVertex2f(x+WIDTH, y+HEIGHT);
GL11.glVertex2f(x, y+WIDTH);
GL11.glEnd();
}
public boolean insideBounds(float x,float x1,float x2)
{
if(x<x1||x>x2)
{
return false;
}
else
{
return true;
}
}
public static boolean moveDown()
{
for(Invader i : Main.getInvaders())
{
if(!i.insideBounds(i.x+(Invader.XSPEED*Invader.DIRECTION),Main.X_BOUND,Main.DISPLAY_WIDTH-Main.X_BOUND-i.WIDTH))
{
return true;
}
}
return false;
}
public static float getDeltaUpdate()
{
return deltaUpdate;
}
public float getX()
{
return x;
}
public float getY()
{
return y;
}
public void setX(float x)
{
this.x=x;
}
public void setY(float y)
{
this.y=y;
}
}
[/lua]
InvaderStandard Class:
[lua]
package spaceinvaders;
public class InvaderStandard extends Invader{
InvaderStandard(float x,float y)
{
setX(x);
setY(y);
}
}
[/lua]
Player Class:
[lua]
package spaceinvaders;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
public class Player {
private float x=240,y=240,w=10,h=10;
List<Bullet> bullets = new ArrayList<Bullet>();
int deltaBullet = 300;
long lastBullet;
public void update(int delta)
{
long time = Main.getTime();
if(Keyboard.isKeyDown(Keyboard.KEY_LEFT)&&
insideBounds((x-0.15f*delta),Main.X_BOUND,Main.DISPLAY_WIDTH-Main.X_BOUND-this.w))
{
x-=0.15*delta;
}
if(Keyboard.isKeyDown(Keyboard.KEY_RIGHT)&&
insideBounds((x+0.15f*delta),Main.X_BOUND,Main.DISPLAY_WIDTH-Main.X_BOUND-this.w))
{
x+=0.15*delta;
}
if(Keyboard.isKeyDown(Keyboard.KEY_SPACE)&&time-lastBullet>deltaBullet)
{
bullets.add(new Bullet(this.x+(this.w/2),this.y));
lastBullet=time;
}
for(Bullet b : bullets)
{
b.update(delta);
}
}
public void render()
{
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex2f(x,y);
GL11.glVertex2f(x+w, y);
GL11.glVertex2f(x+w, y+h);
GL11.glVertex2f(x, y+h);
GL11.glEnd();
}
public boolean insideBounds(float x,float x1,float x2)
{
if(x<x1||x>x2)
{
return false;
}
else
{
return true;
}
}
}
[/lua]
Bullet Class:
[lua]
package spaceinvaders;
import java.awt.Rectangle;
import org.lwjgl.opengl.GL11;
public class Bullet {
private final float Y_SPEED=0.35f;
private final float WIDTH=1,HEIGHT=10;
private float x,y;
Bullet(float x,float y)
{
this.x=x;
this.y=y;
}
public void update(int delta)
{
y-=Y_SPEED*delta;
if(this.y<0)
{
Main.player.bullets.remove(this);
}
else
{
for(Invader i : Main.getInvaders())
{
if(new Rectangle((int)x,(int)y,(int)WIDTH,(int)HEIGHT).intersects(i.getX(),i.getY(),i.WIDTH,i.HEIGHT))
{
Main.getInvaders().remove(i);
Main.player.bullets.remove(this);
}
}
}
}
public void render()
{
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex2f(x,y);
GL11.glVertex2f(x+WIDTH,y);
GL11.glVertex2f(x+WIDTH,y+HEIGHT);
GL11.glVertex2f(x,y+HEIGHT);
GL11.glEnd();
}
}
[/lua]
You're telling the Main.player.bullets to remove the Bullet from the list (Bullet.java:24), while iterating over it (Player.java:35).
That's not allowed and thus throws an exception.
I'd use an Iterator and return false from the update() method to tell, that the bullet should be removed (then you can use iterator.remove() safely)
Ah, so I can't remove a list item while iterating over it? Thanks!
Sorry, you need to Log In to post a reply to this thread.