O que é esse erro?

Alguém pode me explicar o que é esse erro? Por favor.

Parece que vc está tentando adicionar ou remover os elementos de uma coleção dentro da própria iteração. Pode postar o código do laço que vc está fazendo?

Evite postar imagens do erro. É melhor sempre que der, copiar e colar no post (pra mensagens de erro, para códigos tb, etc). E melhor se já fizer a formatação usando o botão do editor </>.

Este?

@Override
    public void run() {
        requestFocus();
        long lastTime = System.nanoTime();
        double amountOfticks = 60.0;
        double ns = 1000000000 / amountOfticks;
        double delta = 0;
        int frames = 0;

        double timer = System.currentTimeMillis();
        while (isRunning) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            if (delta >= 1) {
                tick();
                render();
                frames++;
                delta--;
                if (System.currentTimeMillis() - timer >= 1000) {
                    System.out.println("FPS: " + frames);
                    frames = 0;
                    timer += 1000;
                }
            }
        }
    }

Posta a parte que tem a linha 92 da classe Game.

Eu tenho um arraylist entities que engloba todas as entidades do jogo.
Aqui eu chamo o tick das entidades, que basicamente compõe as animações ou interações da entidade.

 public void tick() {
        for (Entity entity : entities) {
            entity.tick();
        }


    }

Será que em algum lugar (alguma dessas entidades que possuem o tick), não está modificando essa lista entities não?

Eu criei um método de colisão com uma lifepack, será que é isso? :thinking:

public void checkCollisionLifePack() {
        for(int i =0; i <Game.entities.size(); i++) {
            Entity entity = Game.entities.get(i);
            if (entity instanceof Lifepack) {
                if(Entity.isColliding(this,entity)) {
                    life+=30;
                    if (life > maxLife) {
                        life = maxLife;
                    }
                    Game.entities.remove(entity);
                }
            }

        }

    }

Na classe Entity:

public static boolean isColliding(Entity e1, Entity e2) {
        Rectangle boundsE1 = new Rectangle(e1.getX() + e1.maskx, e1.getY() + e1.masky,
                e1.maskw, e1.maskh);
        Rectangle boundsE2 = new Rectangle(e2.getX() + e2.maskx, e2.getY() + e2.masky,
                e2.maskw, e2.maskh);

        return boundsE1.intersects(boundsE2);
    }

Se a chamada do tick no loop estiver chamando esse remove, há grandes chances de ser a causa do erro mesmo.

Faça um teste. coloque um breakpoint nesses removes e qdo parar nesse ponto, veja se o erro estoura logo em seguida.

Adicionei, mas não entendi direito o termo estoura ha ha.

hehe, foi mal. Estourar de “Estourar a exceção”. É quando o erro é lançado pelo sistema. No seu caso, é a exception: ConcurrentModificationException.

Ahh, bem, ele estourou sim.

Uma forma de contornar esse erro é fazer o loop usando um iterator. Veja:

Iterator<Entity> iterator = entities.iterator();

while(iterator.hasNext()) {
	Entity entity = iterator.next();
	entity.tick();
}

Outra forma que talvez fique bom é marcar a Entity para ser removida e fazer a remoção logo após o for que executa o tick.

Pode me explicar melhor a segunda solução?

O que pensei foi. Em vez de vc remover a entity assim:

Game.entities.remove(entity);

Vc poderia marcar ela para remoção usando uma propriedade booleana:

entity.markToRemove(); // esse método iria simplesmente alterar uma propriedade de false pra true

E nessa parte, logo após o for, vc iria percorrer as entities e remover aquelas que foram marcadas.

public void tick() {
  for (Entity entity : entities) {
    entity.tick();
  }

  entities.removeIf(e -> e.isMarkedToRemove());
}

Mas confesso que não sei se é um bom caminho essa ideia, pq vc está desenvolvendo um jogo. Talvez tenha formas melhores de fazer isso nesse seu cenário.

Vou dar uma olhada, muito obrigado.

Apenas para informação, veja:

public class Main {

	public static void main(String[] args) {
		List<String> nomes = new ArrayList<>();
		nomes.add("Aaa");
		nomes.add("Bbb");
		nomes.add("Ccc");
		nomes.add("Ddd");
		nomes.add("Eee");
		
		// Estoura exceção
		for (String n : nomes) {
			if ("Ccc".equals(n)) {
				nomes.remove(n);
			}
		}
		
		// Não dá erro
//		nomes.removeIf((n) -> "Ccc".equals(n));
		
		// Não dá erro
//		Iterator<String> iterator = nomes.iterator();
//		while(iterator.hasNext()) {
//			String n = iterator.next();
//			if ("Ccc".equals(n)) {
//				iterator.remove();
//			}
//		}
		
		System.out.println(nomes);
	}
}

Experimente rodar esse código testando cada parte (vá comentando e descomentando na medida que for executando). A que vc está fazendo é a primeira que estoura erro.

Nos meus games o método add e o remove populam uma lista de objetos a serem adicionados ao jogo e uma lista de objetos a serem removidos.
No método tick, após o loop, eu adiciono na lista principal os elementos da lista de inserção e excluo da lista principal os elementos da lista de remoção.

1 curtida