Inconsistent IndexOutOfBounds Exceptions

Hey,

I’ve been working on a simple space invaders clone since noon and I keep running into inconsistent errors that I have trouble replicating every game that I test. The most common error is with the following line in the loop that follows it.

if(projectiles.get(i).getColissionBox().intersects(entities.get(j).getColissionBox())
// Check if any bullet has collided with any alien entity. If a bullet has then remove it and remove the entity.
		for(int i=0;i<projectiles.size();i++) {
			for(int j=0;j<entities.size();j++) {
				if(projectiles.get(i).getColissionBox().intersects(entities.get(j).getColissionBox())) {
					playerScore += entities.get(j).getSpeed() * 10;
					entities.remove(j); // Remove dead entities.
					projectiles.remove(i); // Remove the projectile that was used to kill the entity.
				}
			}
		}

Below here is the entire class.

package level;

import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import core.KeyboardInputHandler;
import entity.Alien;
import entity.Entity;
import entity.Player;
import entity.projectile.Bullet;
import entity.projectile.Projectile;

public class Level {
	private int playerScore = 0;
	private int levelsCompleted = 0;
	private int playerShootTimer = 100;
	//private BufferedImage background;
	private Player playerEntity;
	private List<Entity> entities = new ArrayList<Entity>();
	private List<Projectile> projectiles = new ArrayList<Projectile>();
	
	public Level() {
		playerEntity = new Player("Player", 512, 448);
	}
	
	public void update(final KeyboardInputHandler KEY_INPUT) {
		if(entities.size() == 0) {
			// Remove all projectiles from the level before spawning new entities.
			int projectilesSize = projectiles.size();
			if(projectilesSize > 0) {
				for(int i=0;i<projectiles.size();i++) {
					projectiles.remove(i);
				}
			}
			
			// Spawn new entities.
			for(int i=0;i<10;i++) {
				for(int j=0;j<5;j++) {
					entities.add((levelsCompleted > 0 ?  new Alien("Creature - "+i, i * 64 , j * 64, levelsCompleted) : new Alien("Creature - "+i, i * 64 , j * 64)));
				}
			}
			
			// Increase the level counter.
			if(playerScore > 0) {
				levelsCompleted++;
			}
		}
		
		// Check if any bullet has collided with any alien entity. If a bullet has then remove it and remove the entity.
		for(int i=0;i<projectiles.size();i++) {
			for(int j=0;j<entities.size();j++) {
				if(projectiles.get(i).getColissionBox().intersects(entities.get(j).getColissionBox())) {
					playerScore += entities.get(j).getSpeed() * 10;
					entities.remove(j); // Remove dead entities.
					projectiles.remove(i); // Remove the projectile that was used to kill the entity.
				}
			}
		}
				
		// Update the player.
		playerEntity.update(KEY_INPUT);
		
		// Update all entities.
		for(int i=0;i<entities.size();i++) {
			entities.get(i).update();
		}
		
		// Remove projectiles if they're off-screen, if it's not off-screen then update it.
		for(int i=0;i<projectiles.size();i++) {
			if(projectiles.get(i).getIsVisible()) {
				projectiles.get(i).update();
			} else {
				projectiles.remove(i);
			}
		}
		
		// If the user presses the space bar and if the timer is at 10 or above then create a new projectile.
		if(KEY_INPUT.isKeyPressed(KeyEvent.VK_SPACE) && playerShootTimer >= 40) {
			// This requires that the player entity be at index 0 of the entities list.
			projectiles.add(new Bullet(playerEntity.getXPosition() + (playerEntity.getSprite().getWidth () / 2), playerEntity.getYPosition(), true, (2 + levelsCompleted)));
			playerShootTimer = 0;
		} else {
			playerShootTimer++;
		}
	}
	
	public void render(final Graphics g) {
		// Draw the background.
		//g.drawImage(background, 0, 0, background.getHeight(), background.getWidth(), null);
				
		
		// Render player.
		playerEntity.render(g);
		
		// Render entities.
		for(int i=0;i<entities.size();i++) {
			entities.get(i).render(g);
		}
		
		// Render projectiles.
		for(int i=0;i<projectiles.size();i++) {
			projectiles.get(i).render(g);
		}
	}
	
	public boolean isGameOver() {
		boolean tempVar = false;
		
		// Check if the game is over yet.
		for(int i=0;i<entities.size();i++) {
			if(entities.get(i).getYPosition() >= 448) {
				tempVar =  true;
				break;
			}
		}
		
		return tempVar;
	}
	
	// Get methods:
	public int getPlayerScore() {
		return playerScore;
	}
}

Thanks in advance if anyone can see some error that I’ve made with the loop or something.

You’re removing the projectile while still checking other entities for collision with it, you need to make sure you move back out to your outer loop after the projectile collides, because it’s no longer there, so checking if it collides with other entities crashes it.
rewrite it as:

// Check if any bullet has collided with any alien entity. If a bullet has then remove it and remove the entity.
      for(int i=0;i<projectiles.size();i++) {
         for(int j=0;j<entities.size();j++) {
            if(projectiles.get(i).getColissionBox().intersects(entities.get(j).getColissionBox())) {
               playerScore += entities.get(j).getSpeed() * 10;
               entities.remove(j); // Remove dead entities.
               projectiles.remove(i); // Remove the projectile that was used to kill the entity.
               break;
            }
         }
      }

there’s no chance that projectile will hit any entities, so you break back to your outer loop.

also the code is wrong, because everytime you delete a projectile you skip the next

Yeah, you should always iterate backward through arraylists. Although, isn’t it bad practice to remove objects while iterating through arrayLists in general?
Totally didn’t even notice that.

Why would it be bad practice? Also, why would you iterate backwards? Never once in my life have I seen anyone do that. ArrayLists are built to be dynamic, you don’t need to worry about simple things like that.

No, because arraylists lose size when you iterate through forward, and it gets compressed so your list looks like:


[0]0
[1]1
[2]2
[3]3
[4]4
[5]5
[6]6
[7]7
[8]8
[9]9

so if you remove say, index 6 it’d look like:

[0]0
[1]1
[2]2
[3]3
[4]4
[5]5
[6]7
[7]8
[8]9

so you’re effectively skipping the 7, because it’s now index 6, instead of index 7.

If you don’t want the size to decrease when removing elements, you should be using an array.

I thought the whole idea of the Arraylist was to be flexible and so you wouldn’t have to worry about size constraints. If you’re using them to hold a list that is order sensitive, then you shouldn’t be. You should be using an array.

I’ve run into this issue before and the best solution I’ve had is instead of calling “remove” inside the for loop, simply add it to
a separate arraylist, such as
ArrayList removeEntities;

then inside your for loop, just do
removeEntities.add(entities.get(j));

then when you are done with both for loop, immediately afterwards call
entities.removeAll(removeEntitities);
removeEntities.clear();

Some people in thread seem to be confused, in general about arraylist and remove, and moving backwards

imagine we have 4 badguys (size() is 4)
badguy1 at i = 0;
badguy2 at i = 1;
badguy3 at i = 2;
badguy4 at i = 3;

Then lets say when you are at i = 1; you want to remove badguy2, sure thats fine, he goes poof he goes away
now because hes gone, we now have
badguy1 at i = 0;
badguy3 at i = 1;
badguy4 at i = 2;
but then you move to end up for loop, and you do i++
so now you are looking at i=2
Which the for loop worked correctly, the for loop will exit correctly at the end, when i < size() is false.
However now you never had a chance to look at badguy3

So thats why if you call remove() on something that is reflecting the .size() then you need ot call an extra --i; so you look at that i=1 case a 2nd time, because there is a different badguy there

or alternatively, do what bilznatch said and do something like
for(int i = entities.size() - 1; i >= 0; i–) and thatd fix a lot of the issues.

Make something like an array of interface called “Action” or something. It should have method called “doAction()” or something. When you want to remove something, add that Action to your arraylist. After you go through all the bullets and aliens, go through all the actions and do what you wanted.

In the other words, remove bullets and/or aliens after you go through entities and bullets lists.

You should also try to put stuff more organized or something. Put the code where you check for collision between bullet and entites into bullet class, so it is more organized.

EDIT:

public class GameClass {
	
	public static ArrayList<Action> actions = new ArrayList<Action>();
	
	public static ArrayList<Entity> entities = new ArrayList<Entity>();
	
	public void tick() {
		
		for(int i=0;i<entities.size();i++) {
			Entity entity = entities.get(i);
			entity.tick();
		}
		
		for(Action action : actions) {
			action.doAction();
		}
		actions.clear();
		
	}
	
}

public class Entity {
	
	public void remove() {
		
		GameClass.actions.add(new Action() {
			
			public void doAction() {
				GameClass.entities.remove(this);
			}
			
		});
	}
	
	public void tick() {
		// update this entity to your liking. Position, HP or something?
	}
	
}

public interface Action {
	public void doAction();
}