1

OK, so I'm developing a small game about bouncing a ball around to hit enemies and power-ups. I got the ball moving and bouncing on walls, enemies and the player character, but I just can't make power-ups disappear upon being touched by the ball.

I've read several threads asking a similar question, and have tried all the solutions that were proposed: setting the power-up object to null, removing it from the ArrayList it's stored in with remove(), storing its location in the ArrayList to remove it later using this location... I've even tried to set up a system of ids to remove it from the ArrayList by using its unique ID. But the result is always the same: when the ball hits the power-up, the game freezes and it throws a ConcurrentModificationException.

(The method tick() in the class PowerUp below is called 60 times per second for each power-up in the ArrayList powerups; getBounds() returns a Rectangle with the entity's coordinates and dimensions, it works perfectly).

public class Game {

    public static ArrayList<PowerUp> powerups = new ArrayList<PowerUp>();

}

public class PowerUp {

    public void tick(){
        if(this.getBounds().intersects(Game.gameBall.getBounds())){
            this.activate();
        }
    }

    private void activate(){
        //do whatever
        //delete this from the game
        Game.powerups.remove(this);//this is what I tried first
    }
}

So, how do I delete the power-up without getting that damnable exception?

VACN
  • 75
  • 8
  • How are you drawing your scene? – matt Jan 16 '20 at 16:13
  • 1
    This error usually happens when you are trying to remove something from a list, while itrerating over it, see this question : https://stackoverflow.com/questions/3184883/concurrentmodificationexception-for-arraylist – Arnaud Jan 16 '20 at 16:14
  • I also call a render() method 60 times per second for each entity. – VACN Jan 16 '20 at 16:14
  • read: https://www.baeldung.com/java-concurrentmodificationexception – JavaBoy Jan 16 '20 at 16:15
  • @Arnaud is right. The easiest way to solve this is to make sure you only ever *either* change the game state *or* paint the current game state and never both at once. The simplest way to do that is via synchronization. – Joachim Sauer Jan 16 '20 at 16:15
  • @VACN was that an answer to me? What is happening in your render loop. Most likely, render is looping over your list to draw them. for(PowerUp power: powerups){...} and while that loop is happening, you get this error. – matt Jan 16 '20 at 16:18
  • The Game class has a tick() method that loops through all power-ups in the ArrayList and calls a tick() method on each one. It also has a render() method that loops through all power-ups in the ArrayList and calls a render() method on each one. The game doesn't alternate between ticks and renders, it does all ticks first, then all renders. – VACN Jan 16 '20 at 16:20
  • @Andrea Using an iterator doesn't solve the issue. Still getting that exception. – VACN Jan 16 '20 at 16:21
  • @VACN an iterator would help, but you have to call iterator.remove. You are doing `for(PowerUp pu: powerups){ pu.tick();}` inside of tick you cannot call List#remove it will throw the CME. – matt Jan 16 '20 at 16:23
  • @VACN give your power up a field 'used/finished/pleaseRemove' then remove all of the ones that have that field after you've finished the loop. – matt Jan 16 '20 at 16:29
  • @matt But I'm still gonna have to remove them from the ArrayList while looping through it, am I right? – VACN Jan 16 '20 at 16:33
  • @VACN Not with for(PowerUp...) loop. You could use powerups.removeIf(PowerUp::hasBeenActivated), then create a method that determines if the pu has been activated. – matt Jan 16 '20 at 16:42
  • You could add elements to another list if their removeable. if(pu.isRemovable()){toRemove.add(pu);}. then afterwards. powerups.removeAll(toRemove); The possibilities are numerous. – matt Jan 16 '20 at 16:43
  • @matt I just tried that, and though it doesn't throw any exceptions, the power-up stubbornly keeps existing. The removal has no effect. – VACN Jan 16 '20 at 17:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206080/discussion-between-matt-and-vacn). – matt Jan 16 '20 at 17:03

1 Answers1

1

This is because you are removing an object from the list while iterating over it. Use a CopyOnWriteArrayList instead:

public class Game {

    public static List<PowerUp> powerups = new CopyOnWriteArrayList<PowerUp>();

}

public class PowerUp {

    public void tick(){
        if(this.getBounds().intersects(Game.gameBall.getBounds())){
            this.activate();
        }
    }

    private void activate(){
        //do whatever
        //delete this from the game
        Game.powerups.remove(this);//this is what I tried first
    }
}
JavaBoy
  • 182
  • 7
  • "Exception in thread "Thread-4" java.lang.UnsupportedOperationException at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(Unknown Source)" – VACN Jan 16 '20 at 16:23
  • You are trying to remove a PowerUp that is not in that list – JavaBoy Jan 18 '20 at 14:04